source: trunk/minix/commands/simple/fsck.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 40.9 KB
Line 
1/* Hacks for version 1.6 */
2
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 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
21
22/* fsck - file system checker Author: Robbert van Renesse */
23
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
31*
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!
36*/
37
38#include <sys/types.h>
39#include <sys/dir.h>
40#include <ctype.h>
41#include <errno.h>
42#include <fcntl.h>
43#include <limits.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <minix/config.h>
48#include <minix/const.h>
49#include <minix/type.h>
50#include "../../servers/fs/const.h"
51#include "../../servers/fs/inode.h"
52#include "../../servers/fs/type.h"
53#include <minix/fslib.h>
54#include <stdio.h>
55#include <sys/stat.h>
56#include <a.out.h>
57#include <tools.h>
58#include <dirent.h>
59
60#undef N_DATA
61
62unsigned int fs_version = 2, block_size = 0;
63
64#define BITSHIFT 4 /* = log2(#bits(int)) */
65
66#define MAXPRINT 80 /* max. number of error lines in chkmap */
67#define CINDIR 128 /* number of indirect zno's read at a time */
68#define CDIRECT 1 /* number of dir entries read at a time */
69
70/* Macros for handling bitmaps. Now bit_t is long, these are bulky and the
71 * type demotions produce a lot of lint. The explicit demotion in POWEROFBIT
72 * is for efficiency and assumes 2's complement ints. Lint should be clever
73 * enough not to warn about it since BITMASK is small, but isn't. (It would
74 * be easier to get right if bit_t was was unsigned (long) since then there
75 * would be no danger from wierd sign representations. Lint doesn't know
76 * we only use non-negative bit numbers.) There will usually be an implicit
77 * demotion when WORDOFBIT is used as an array index. This should be safe
78 * since memory for bitmaps will run out first.
79 */
80#define BITMASK ((1 << BITSHIFT) - 1)
81#define WORDOFBIT(b) ((b) >> BITSHIFT)
82#define POWEROFBIT(b) (1 << ((int) (b) & BITMASK))
83#define setbit(w, b) (w[WORDOFBIT(b)] |= POWEROFBIT(b))
84#define clrbit(w, b) (w[WORDOFBIT(b)] &= ~POWEROFBIT(b))
85#define bitset(w, b) (w[WORDOFBIT(b)] & POWEROFBIT(b))
86
87#define ZONE_CT 360 /* default zones (when making file system) */
88#define INODE_CT 95 /* default inodes (when making file system) */
89
90#include "../../servers/fs/super.h"
91static struct super_block sb;
92
93#define STICKY_BIT 01000 /* not defined anywhere else */
94
95/* Ztob gives the block address of a zone
96 * btoa gives the byte address of a block
97 */
98#define ztob(z) ((block_nr) (z) << sb.s_log_zone_size)
99#define btoa(b) ((long) (b) * block_size)
100#define SCALE ((int) ztob(1)) /* # blocks in a zone */
101#define FIRST ((zone_nr) sb.s_firstdatazone) /* as the name says */
102
103/* # blocks of each type */
104#define N_IMAP (sb.s_imap_blocks)
105#define N_ZMAP (sb.s_zmap_blocks)
106#define N_ILIST ((sb.s_ninodes+INODES_PER_BLOCK-1) / INODES_PER_BLOCK)
107#define N_DATA (sb.s_zones - FIRST)
108
109/* Block address of each type */
110#define OFFSET_SUPER_BLOCK SUPER_BLOCK_BYTES
111#define BLK_IMAP 2
112#define BLK_ZMAP (BLK_IMAP + N_IMAP)
113#define BLK_ILIST (BLK_ZMAP + N_ZMAP)
114#define BLK_FIRST ztob(FIRST)
115#define ZONE_SIZE ((int) ztob(block_size))
116#define NLEVEL (NR_ZONE_NUMS - NR_DZONE_NUM + 1)
117
118/* Byte address of a zone/of an inode */
119#define zaddr(z) btoa(ztob(z))
120#define cinoaddr(i) ((long) (i - 1) * INODE_SIZE + (long) btoa(BLK_ILIST))
121#define INDCHUNK ((int) (CINDIR * ZONE_NUM_SIZE))
122#define DIRCHUNK ((int) (CDIRECT * DIR_ENTRY_SIZE))
123
124char *prog, *fsck_device; /* program name (fsck), device name */
125int firstcnterr; /* is this the first inode ref cnt error? */
126bitchunk_t *imap, *spec_imap; /* inode bit maps */
127bitchunk_t *zmap, *spec_zmap; /* zone bit maps */
128bitchunk_t *dirmap; /* directory (inode) bit map */
129char *rwbuf; /* one block buffer cache */
130block_nr thisblk; /* block in buffer cache */
131char *nullbuf; /* null buffer */
132nlink_t *count; /* inode count */
133int changed; /* has the diskette been written to? */
134struct stack {
135 dir_struct *st_dir;
136 struct stack *st_next;
137 char st_presence;
138} *ftop;
139
140int dev; /* file descriptor of the device */
141
142#define DOT 1
143#define DOTDOT 2
144
145/* Counters for each type of inode/zone. */
146int nfreeinode, nregular, ndirectory, nblkspec, ncharspec, nbadinode;
147int npipe, nsyml, ztype[NLEVEL];
148long nfreezone;
149
150int repair, automatic, listing, listsuper; /* flags */
151int firstlist; /* has the listing header been printed? */
152unsigned part_offset; /* sector offset for this partition */
153char answer[] = "Answer questions with y or n. Then hit RETURN";
154
155_PROTOTYPE(int main, (int argc, char **argv));
156_PROTOTYPE(void initvars, (void));
157_PROTOTYPE(void fatal, (char *s));
158_PROTOTYPE(int eoln, (int c));
159_PROTOTYPE(int yes, (char *question));
160_PROTOTYPE(int atoo, (char *s));
161_PROTOTYPE(int input, (char *buf, int size));
162_PROTOTYPE(char *alloc, (unsigned nelem, unsigned elsize));
163_PROTOTYPE(void printname, (char *s));
164_PROTOTYPE(void printrec, (struct stack *sp));
165_PROTOTYPE(void printpath, (int mode, int nlcr));
166_PROTOTYPE(void devopen, (void));
167_PROTOTYPE(void devclose, (void));
168_PROTOTYPE(void devio, (block_nr bno, int dir));
169_PROTOTYPE(void devread, (long offset, char *buf, int size));
170_PROTOTYPE(void devwrite, (long offset, char *buf, int size));
171_PROTOTYPE(void pr, (char *fmt, int cnt, char *s, char *p));
172_PROTOTYPE(void lpr, (char *fmt, long cnt, char *s, char *p));
173_PROTOTYPE(bit_nr getnumber, (char *s));
174_PROTOTYPE(char **getlist, (char ***argv, char *type));
175_PROTOTYPE(void lsuper, (void));
176_PROTOTYPE(void getsuper, (void));
177_PROTOTYPE(void chksuper, (void));
178_PROTOTYPE(void lsi, (char **clist));
179_PROTOTYPE(bitchunk_t *allocbitmap, (int nblk));
180_PROTOTYPE(void loadbitmap, (bitchunk_t *bitmap, block_nr bno, int nblk));
181_PROTOTYPE(void dumpbitmap, (bitchunk_t *bitmap, block_nr bno, int nblk));
182_PROTOTYPE(void fillbitmap, (bitchunk_t *bitmap, bit_nr lwb, bit_nr upb, char **list));
183_PROTOTYPE(void freebitmap, (bitchunk_t *p));
184_PROTOTYPE(void getbitmaps, (void));
185_PROTOTYPE(void putbitmaps, (void));
186_PROTOTYPE(void chkword, (unsigned w1, unsigned w2, bit_nr bit, char *type, int *n, int *report, bit_t));
187_PROTOTYPE(void chkmap, (bitchunk_t *cmap, bitchunk_t *dmap, bit_nr bit, block_nr blkno, int nblk, char *type));
188_PROTOTYPE(void chkilist, (void));
189_PROTOTYPE(void getcount, (void));
190_PROTOTYPE(void counterror, (Ino_t ino));
191_PROTOTYPE(void chkcount, (void));
192_PROTOTYPE(void freecount, (void));
193_PROTOTYPE(void printperm, (mode_t mode, int shift, int special, int overlay));
194_PROTOTYPE(void list, (Ino_t ino, d_inode *ip));
195_PROTOTYPE(int Remove, (dir_struct *dp));
196_PROTOTYPE(void make_printable_name, (char *dst, char *src, int n));
197_PROTOTYPE(int chkdots, (Ino_t ino, off_t pos, dir_struct *dp, Ino_t exp));
198_PROTOTYPE(int chkname, (Ino_t ino, dir_struct *dp));
199_PROTOTYPE(int chkentry, (Ino_t ino, off_t pos, dir_struct *dp));
200_PROTOTYPE(int chkdirzone, (Ino_t ino, d_inode *ip, off_t pos, zone_nr zno));
201_PROTOTYPE(int chksymlinkzone, (Ino_t ino, d_inode *ip, off_t pos,
202 zone_nr zno));
203_PROTOTYPE(void errzone, (char *mess, zone_nr zno, int level, off_t pos));
204_PROTOTYPE(int markzone, (zone_nr zno, int level, off_t pos));
205_PROTOTYPE(int chkindzone, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level));
206_PROTOTYPE(off_t jump, (int level));
207_PROTOTYPE(int zonechk, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level));
208_PROTOTYPE(int chkzones, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist, int len, int level));
209_PROTOTYPE(int chkfile, (Ino_t ino, d_inode *ip));
210_PROTOTYPE(int chkdirectory, (Ino_t ino, d_inode *ip));
211_PROTOTYPE(int chklink, (Ino_t ino, d_inode *ip));
212_PROTOTYPE(int chkspecial, (Ino_t ino, d_inode *ip));
213_PROTOTYPE(int chkmode, (Ino_t ino, d_inode *ip));
214_PROTOTYPE(int chkinode, (Ino_t ino, d_inode *ip));
215_PROTOTYPE(int descendtree, (dir_struct *dp));
216_PROTOTYPE(void chktree, (void));
217_PROTOTYPE(void printtotal, (void));
218_PROTOTYPE(void chkdev, (char *f, char **clist, char **ilist, char **zlist));
219
220/* Initialize the variables used by this program. */
221void initvars()
222{
223 register level;
224
225 nregular = ndirectory = nblkspec = ncharspec = nbadinode = npipe = nsyml = 0;
226 for (level = 0; level < NLEVEL; level++) ztype[level] = 0;
227 changed = 0;
228 thisblk = NO_BLOCK;
229 firstlist = 1;
230 firstcnterr = 1;
231}
232
233/* Print the string `s' and exit. */
234void fatal(s)
235char *s;
236{
237 printf("%s\nfatal\n", s);
238 exit(-1);
239}
240
241/* Test for end of line. */
242int eoln(c)
243int c;
244{
245 return(c == EOF || c == '\n' || c == '\r');
246}
247
248/* Ask a question and get the answer unless automatic is set. */
249int yes(question)
250char *question;
251{
252 register int c, answerchar;
253 static int note = 0;
254
255 if (!repair) {
256 printf("\n");
257 return(0);
258 }
259 printf("%s? ", question);
260 if(!note) { printf("(y=yes, n=no, q=quit, A=for yes to all) "); note = 1; }
261 if (automatic) {
262 printf("yes\n");
263 return(1);
264 }
265 fflush(stdout);
266 if ((c = answerchar = getchar()) == 'q' || c == 'Q') exit(1);
267 if(c == 'A') { automatic = 1; c = 'y'; }
268 while (!eoln(c)) c = getchar();
269 return !(answerchar == 'n' || answerchar == 'N');
270}
271
272/* Convert string to integer. Representation is octal. */
273int atoo(s)
274register char *s;
275{
276 register int n = 0;
277
278 while ('0' <= *s && *s < '8') {
279 n <<= 3;
280 n += *s++ - '0';
281 }
282 return n;
283}
284
285/* If repairing the file system, print a prompt and get a string from user. */
286int input(buf, size)
287char *buf;
288int size;
289{
290 register char *p = buf;
291
292 printf("\n");
293 if (repair) {
294 printf("--> ");
295 fflush(stdout);
296 while (--size) {
297 *p = getchar();
298 if (eoln(*p)) {
299 *p = 0;
300 return(p > buf);
301 }
302 p++;
303 }
304 *p = 0;
305 while (!eoln(getchar()));
306 return(1);
307 }
308 return(0);
309}
310
311/* Allocate some memory and zero it. */
312char *alloc(nelem, elsize)
313unsigned nelem, elsize;
314{
315 char *p;
316
317 if ((p = (char *)malloc((size_t)nelem * elsize)) == 0) {
318 fprintf(stderr, "Tried to allocate %dkB\n",
319 nelem*elsize/1024);
320 fatal("out of memory");
321 }
322 memset((void *) p, 0, (size_t)nelem * elsize);
323 return(p);
324}
325
326/* Print the name in a directory entry. */
327void printname(s)
328char *s;
329{
330 register n = NAME_MAX;
331 int c;
332
333 do {
334 if ((c = *s) == 0) break;
335 if (!isprint(c)) c = '?';
336 putchar(c);
337 s++;
338 } while (--n);
339}
340
341/* Print the pathname given by a linked list pointed to by `sp'. The
342 * names are in reverse order.
343 */
344void printrec(sp)
345struct stack *sp;
346{
347 if (sp->st_next != 0) {
348 printrec(sp->st_next);
349 putchar('/');
350 printname(sp->st_dir->d_name);
351 }
352}
353
354/* Print the current pathname. */
355void printpath(mode, nlcr)
356int mode;
357int nlcr;
358{
359 if (ftop->st_next == 0)
360 putchar('/');
361 else
362 printrec(ftop);
363 switch (mode) {
364 case 1:
365 printf(" (ino = %u, ", ftop->st_dir->d_inum);
366 break;
367 case 2:
368 printf(" (ino = %u)", ftop->st_dir->d_inum);
369 break;
370 }
371 if (nlcr) printf("\n");
372}
373
374/* Open the device. */
375void devopen()
376{
377 if ((dev = open(fsck_device, repair ? O_RDWR : O_RDONLY)) < 0) {
378 perror(fsck_device);
379 fatal("couldn't open device to fsck");
380 }
381}
382
383/* Close the device. */
384void devclose()
385{
386 if (close(dev) != 0) {
387 perror("close");
388 fatal("");
389 }
390}
391
392/* Read or write a block. */
393void devio(bno, dir)
394block_nr bno;
395int dir;
396{
397 if(!block_size) fatal("devio() with unknown block size");
398 if (dir == READING && bno == thisblk) return;
399 thisblk = bno;
400
401#if 0
402printf("%s at block %5d\n", dir == READING ? "reading " : "writing", bno);
403#endif
404 lseek(dev, (off_t) btoa(bno), SEEK_SET);
405 if (dir == READING) {
406 if (read(dev, rwbuf, block_size) == block_size)
407 return;
408 } else {
409 if (write(dev, rwbuf, block_size) == block_size)
410 return;
411 }
412
413 printf("%s: can't %s block %ld (error = 0x%x)\n", prog,
414 dir == READING ? "read" : "write", (long) bno, errno);
415 if (dir == READING) {
416 printf("Continuing with a zero-filled block.\n");
417 memset(rwbuf, 0, block_size);
418 return;
419 }
420 fatal("");
421}
422
423/* Read `size' bytes from the disk starting at byte `offset'. */
424void devread(offset, buf, size)
425long offset;
426char *buf;
427int size;
428{
429 if(!block_size) fatal("devread() with unknown block size");
430 devio((block_nr) (offset / block_size), READING);
431 memmove(buf, &rwbuf[(int) (offset % block_size)], (size_t)size); /* lint but OK */
432}
433
434/* Write `size' bytes to the disk starting at byte `offset'. */
435void devwrite(offset, buf, size)
436long offset;
437char *buf;
438int size;
439{
440 if(!block_size) fatal("devwrite() with unknown block size");
441 if (!repair) fatal("internal error (devwrite)");
442 if (size != block_size) devio((block_nr) (offset / block_size), READING);
443 memmove(&rwbuf[(int) (offset % block_size)], buf, (size_t)size); /* lint but OK */
444 devio((block_nr) (offset / block_size), WRITING);
445 changed = 1;
446}
447
448/* Print a string with either a singular or a plural pronoun. */
449void pr(fmt, cnt, s, p)
450char *fmt, *s, *p;
451int cnt;
452{
453 printf(fmt, cnt, cnt == 1 ? s : p);
454}
455
456/* Same as above, but with a long argument */
457void lpr(fmt, cnt, s, p)
458char *fmt, *s, *p;
459long cnt;
460{
461 printf(fmt, cnt, cnt == 1 ? s : p);
462}
463
464/* Convert string to number. */
465bit_nr getnumber(s)
466register char *s;
467{
468 register bit_nr n = 0;
469
470 if (s == NULL)
471 return NO_BIT;
472 while (isdigit(*s))
473 n = (n << 1) + (n << 3) + *s++ - '0';
474 return (*s == '\0') ? n : NO_BIT;
475}
476
477/* See if the list pointed to by `argv' contains numbers. */
478char **getlist(argv, type)
479char ***argv, *type;
480{
481 register char **list = *argv;
482 register empty = 1;
483
484 while (getnumber(**argv) != NO_BIT) {
485 (*argv)++;
486 empty = 0;
487 }
488 if (empty) {
489 printf("warning: no %s numbers given\n", type);
490 return(NULL);
491 }
492 return(list);
493}
494
495/* Make a listing of the super block. If `repair' is set, ask the user
496 * for changes.
497 */
498void lsuper()
499{
500 char buf[80];
501
502 do {
503 /* Most of the following atol's enrage lint, for good reason. */
504 printf("ninodes = %u", sb.s_ninodes);
505 if (input(buf, 80)) sb.s_ninodes = atol(buf);
506 printf("nzones = %ld", sb.s_zones);
507 if (input(buf, 80)) sb.s_zones = atol(buf);
508 printf("imap_blocks = %u", sb.s_imap_blocks);
509 if (input(buf, 80)) sb.s_imap_blocks = atol(buf);
510 printf("zmap_blocks = %u", sb.s_zmap_blocks);
511 if (input(buf, 80)) sb.s_zmap_blocks = atol(buf);
512 printf("firstdatazone = %u", sb.s_firstdatazone);
513 if (input(buf, 80)) sb.s_firstdatazone = atol(buf);
514 printf("log_zone_size = %u", sb.s_log_zone_size);
515 if (input(buf, 80)) sb.s_log_zone_size = atol(buf);
516 printf("maxsize = %ld", sb.s_max_size);
517 if (input(buf, 80)) sb.s_max_size = atol(buf);
518 printf("block size = %ld", sb.s_block_size);
519 if (input(buf, 80)) sb.s_block_size = atol(buf);
520 if (yes("ok now")) {
521 devwrite(OFFSET_SUPER_BLOCK, (char *) &sb, sizeof(sb));
522 return;
523 }
524 } while (yes("Do you want to try again"));
525 if (repair) exit(0);
526}
527
528/* Get the super block from either disk or user. Do some initial checks. */
529void getsuper()
530{
531 if(lseek(dev, OFFSET_SUPER_BLOCK, SEEK_SET) < 0) {
532 perror("lseek");
533 fatal("couldn't seek to super block.");
534 }
535 if(read(dev, &sb, sizeof(sb)) != sizeof(sb)) {
536 fatal("couldn't read super block.");
537 }
538 if (listsuper) lsuper();
539 if (sb.s_magic == SUPER_MAGIC) fatal("Cannot handle V1 file systems");
540 if (sb.s_magic == SUPER_V2) {
541 fs_version = 2;
542 block_size = /* STATIC_BLOCK_SIZE */ 8192;
543 } else if(sb.s_magic == SUPER_V3) {
544 fs_version = 3;
545 block_size = sb.s_block_size;
546 } else {
547 fatal("bad magic number in super block");
548 }
549 if (sb.s_ninodes <= 0) fatal("no inodes");
550 if (sb.s_zones <= 0) fatal("no zones");
551 if (sb.s_imap_blocks <= 0) fatal("no imap");
552 if (sb.s_zmap_blocks <= 0) fatal("no zmap");
553 if (sb.s_firstdatazone <= 4) fatal("first data zone too small");
554 if (sb.s_log_zone_size < 0) fatal("zone size < block size");
555 if (sb.s_max_size <= 0) fatal("max. file size <= 0");
556
557}
558
559/* Check the super block for reasonable contents. */
560void chksuper()
561{
562 register n;
563 register off_t maxsize;
564
565 n = bitmapsize((bit_t) sb.s_ninodes + 1, block_size);
566 if (sb.s_magic != SUPER_V2 && sb.s_magic != SUPER_V3)
567 fatal("bad magic number in super block");
568 if (sb.s_imap_blocks < n) {
569 printf("need %d bocks for inode bitmap; only have %d\n",
570 n, sb.s_imap_blocks);
571 fatal("too few imap blocks");
572 }
573 if (sb.s_imap_blocks != n) {
574 pr("warning: expected %d imap_block%s", n, "", "s");
575 printf(" instead of %d\n", sb.s_imap_blocks);
576 }
577 n = bitmapsize((bit_t) sb.s_zones, block_size);
578 if (sb.s_zmap_blocks < n) fatal("too few zmap blocks");
579 if (sb.s_zmap_blocks != n) {
580 pr("warning: expected %d zmap_block%s", n, "", "s");
581 printf(" instead of %d\n", sb.s_zmap_blocks);
582 }
583 if (sb.s_firstdatazone >= sb.s_zones)
584 fatal("first data zone too large");
585 if (sb.s_log_zone_size >= 8 * sizeof(block_nr))
586 fatal("log_zone_size too large");
587 if (sb.s_log_zone_size > 8) printf("warning: large log_zone_size (%d)\n",
588 sb.s_log_zone_size);
589 n = (BLK_ILIST + N_ILIST + SCALE - 1) >> sb.s_log_zone_size;
590 if (sb.s_firstdatazone < n) fatal("first data zone too small");
591 if (sb.s_firstdatazone != n) {
592 printf("warning: expected first data zone to be %d ", n);
593 printf("instead of %u\n", sb.s_firstdatazone);
594 }
595 maxsize = MAX_FILE_POS;
596 if (((maxsize - 1) >> sb.s_log_zone_size) / block_size >= MAX_ZONES)
597 maxsize = ((long) MAX_ZONES * block_size) << sb.s_log_zone_size;
598 if (sb.s_max_size != maxsize) {
599 printf("warning: expected max size to be %ld ", maxsize);
600 printf("instead of %ld\n", sb.s_max_size);
601 }
602}
603
604int inoaddr(int inn)
605{
606 int a;
607 a = cinoaddr(inn);
608 return a;
609}
610
611/* Make a listing of the inodes given by `clist'. If `repair' is set, ask
612 * the user for changes.
613 */
614void lsi(clist)
615char **clist;
616{
617 register bit_nr bit;
618 register ino_t ino;
619 d_inode inode, *ip = &inode;
620 char buf[80];
621
622 if (clist == 0) return;
623 while ((bit = getnumber(*clist++)) != NO_BIT) {
624 setbit(spec_imap, bit);
625 ino = bit;
626 do {
627 devread(inoaddr(ino), (char *) ip, INODE_SIZE);
628 printf("inode %u:\n", ino);
629 printf(" mode = %6o", ip->i_mode);
630 if (input(buf, 80)) ip->i_mode = atoo(buf);
631 printf(" nlinks = %6u", ip->i_nlinks);
632 if (input(buf, 80)) ip->i_nlinks = atol(buf);
633 printf(" size = %6ld", ip->i_size);
634 if (input(buf, 80)) ip->i_size = atol(buf);
635 if (yes("Write this back")) {
636 devwrite(inoaddr(ino), (char *) ip, INODE_SIZE);
637 break;
638 }
639 } while (yes("Do you want to change it again"));
640 }
641}
642
643/* Allocate `nblk' blocks worth of bitmap. */
644bitchunk_t *allocbitmap(nblk)
645int nblk;
646{
647 register bitchunk_t *bitmap;
648
649 bitmap = (bitchunk_t *) alloc((unsigned) nblk, block_size);
650 *bitmap |= 1;
651 return(bitmap);
652}
653
654/* Load the bitmap starting at block `bno' from disk. */
655void loadbitmap(bitmap, bno, nblk)
656bitchunk_t *bitmap;
657block_nr bno;
658int nblk;
659{
660 register i;
661 register bitchunk_t *p;
662
663 p = bitmap;
664 for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
665 devread(btoa(bno), (char *) p, block_size);
666 *bitmap |= 1;
667}
668
669/* Write the bitmap starting at block `bno' to disk. */
670void dumpbitmap(bitmap, bno, nblk)
671bitchunk_t *bitmap;
672block_nr bno;
673int nblk;
674{
675 register i;
676 register bitchunk_t *p = bitmap;
677
678 for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
679 devwrite(btoa(bno), (char *) p, block_size);
680}
681
682/* Set the bits given by `list' in the bitmap. */
683void fillbitmap(bitmap, lwb, upb, list)
684bitchunk_t *bitmap;
685bit_nr lwb, upb;
686char **list;
687{
688 register bit_nr bit;
689
690 if (list == 0) return;
691 while ((bit = getnumber(*list++)) != NO_BIT)
692 if (bit < lwb || bit >= upb) {
693 if (bitmap == spec_imap)
694 printf("inode number %ld ", bit);
695 else
696 printf("zone number %ld ", bit);
697 printf("out of range (ignored)\n");
698 } else
699 setbit(bitmap, bit - lwb + 1);
700}
701
702/* Deallocate the bitmap `p'. */
703void freebitmap(p)
704bitchunk_t *p;
705{
706 free((char *) p);
707}
708
709/* Get all the bitmaps used by this program. */
710void getbitmaps()
711{
712 imap = allocbitmap(N_IMAP);
713 zmap = allocbitmap(N_ZMAP);
714 spec_imap = allocbitmap(N_IMAP);
715 spec_zmap = allocbitmap(N_ZMAP);
716 dirmap = allocbitmap(N_IMAP);
717}
718
719/* Release all the space taken by the bitmaps. */
720void putbitmaps()
721{
722 freebitmap(imap);
723 freebitmap(zmap);
724 freebitmap(spec_imap);
725 freebitmap(spec_zmap);
726 freebitmap(dirmap);
727}
728
729/* `w1' and `w2' are differing words from two bitmaps that should be
730 * identical. Print what's the matter with them.
731 */
732void chkword(w1, w2, bit, type, n, report, phys)
733unsigned w1, w2;
734char *type;
735bit_nr bit;
736int *n, *report;
737bit_nr phys;
738{
739 for (; (w1 | w2); w1 >>= 1, w2 >>= 1, bit++, phys++)
740 if ((w1 ^ w2) & 1 && ++(*n) % MAXPRINT == 0 && *report &&
741 (!repair || automatic || yes("stop this listing")))
742 *report = 0;
743 else if (*report)
744 if ((w1 & 1) && !(w2 & 1))
745 printf("%s %ld (%ld) is missing\n", type, bit, phys);
746 else if (!(w1 & 1) && (w2 & 1))
747 printf("%s %ld (%ld) is not free\n", type, bit, phys);
748}
749
750/* Check if the given (correct) bitmap is identical with the one that is
751 * on the disk. If not, ask if the disk should be repaired.
752 */
753void chkmap(cmap, dmap, bit, blkno, nblk, type)
754bitchunk_t *cmap, *dmap;
755bit_nr bit;
756block_nr blkno;
757int nblk;
758char *type;
759{
760 register bitchunk_t *p = dmap, *q = cmap;
761 int report = 1, nerr = 0;
762 int w = nblk * WORDS_PER_BLOCK;
763 bit_nr phys = 0;
764
765 printf("Checking %s map\n", type);
766 loadbitmap(dmap, blkno, nblk);
767 do {
768 if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report, phys);
769 p++;
770 q++;
771 bit += 8 * sizeof(bitchunk_t);
772 phys += 8 * sizeof(bitchunk_t);
773 } while (--w > 0);
774
775 if ((!repair || automatic) && !report) printf("etc. ");
776 if (nerr > MAXPRINT || nerr > 10) printf("%d errors found. ", nerr);
777 if (nerr != 0 && yes("install a new map")) dumpbitmap(cmap, blkno, nblk);
778 if (nerr > 0) printf("\n");
779}
780
781/* See if the inodes that aren't allocated are cleared. */
782void chkilist()
783{
784 register ino_t ino = 1;
785 mode_t mode;
786
787 printf("Checking inode list\n");
788 do
789 if (!bitset(imap, (bit_nr) ino)) {
790 devread(inoaddr(ino), (char *) &mode, sizeof(mode));
791 if (mode != I_NOT_ALLOC) {
792 printf("mode inode %u not cleared", ino);
793 if (yes(". clear")) devwrite(inoaddr(ino), nullbuf,
794 INODE_SIZE);
795 }
796 }
797 while (++ino <= sb.s_ninodes && ino != 0);
798 printf("\n");
799}
800
801/* Allocate an array to maintain the inode reference counts in. */
802void getcount()
803{
804 count = (nlink_t *) alloc((unsigned) (sb.s_ninodes + 1), sizeof(nlink_t));
805}
806
807/* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */
808void counterror(ino)
809ino_t ino;
810{
811 d_inode inode;
812
813 if (firstcnterr) {
814 printf("INODE NLINK COUNT\n");
815 firstcnterr = 0;
816 }
817 devread(inoaddr(ino), (char *) &inode, INODE_SIZE);
818 count[ino] += inode.i_nlinks; /* it was already subtracted; add it back */
819 printf("%5u %5u %5u", ino, (unsigned) inode.i_nlinks, count[ino]);
820 if (yes(" adjust")) {
821 if ((inode.i_nlinks = count[ino]) == 0) {
822 fatal("internal error (counterror)");
823 inode.i_mode = I_NOT_ALLOC;
824 clrbit(imap, (bit_nr) ino);
825 }
826 devwrite(inoaddr(ino), (char *) &inode, INODE_SIZE);
827 }
828}
829
830/* Check if the reference count of the inodes are correct. The array `count'
831 * is maintained as follows: an entry indexed by the inode number is
832 * incremented each time a link is found; when the inode is read the link
833 * count in there is substracted from the corresponding entry in `count'.
834 * Thus, when the whole file system has been traversed, all the entries
835 * should be zero.
836 */
837void chkcount()
838{
839 register ino_t ino;
840
841 for (ino = 1; ino <= sb.s_ninodes && ino != 0; ino++)
842 if (count[ino] != 0) counterror(ino);
843 if (!firstcnterr) printf("\n");
844}
845
846/* Deallocate the `count' array. */
847void freecount()
848{
849 free((char *) count);
850}
851
852/* Print the inode permission bits given by mode and shift. */
853void printperm(mode_t mode, int shift, int special, int overlay)
854{
855 if (mode >> shift & R_BIT)
856 putchar('r');
857 else
858 putchar('-');
859 if (mode >> shift & W_BIT)
860 putchar('w');
861 else
862 putchar('-');
863 if (mode & special)
864 putchar(overlay);
865 else
866 if (mode >> shift & X_BIT)
867 putchar('x');
868 else
869 putchar('-');
870}
871
872/* List the given inode. */
873void list(ino, ip)
874ino_t ino;
875d_inode *ip;
876{
877 if (firstlist) {
878 firstlist = 0;
879 printf(" inode permission link size name\n");
880 }
881 printf("%6u ", ino);
882 switch (ip->i_mode & I_TYPE) {
883 case I_REGULAR: putchar('-'); break;
884 case I_DIRECTORY: putchar('d'); break;
885 case I_CHAR_SPECIAL: putchar('c'); break;
886 case I_BLOCK_SPECIAL: putchar('b'); break;
887 case I_NAMED_PIPE: putchar('p'); break;
888#ifdef I_SYMBOLIC_LINK
889 case I_SYMBOLIC_LINK: putchar('l'); break;
890#endif
891 default: putchar('?');
892}
893 printperm(ip->i_mode, 6, I_SET_UID_BIT, 's');
894 printperm(ip->i_mode, 3, I_SET_GID_BIT, 's');
895 printperm(ip->i_mode, 0, STICKY_BIT, 't');
896 printf(" %3u ", ip->i_nlinks);
897 switch (ip->i_mode & I_TYPE) {
898 case I_CHAR_SPECIAL:
899 case I_BLOCK_SPECIAL:
900 printf(" %2x,%2x ", (dev_t) ip->i_zone[0] >> MAJOR & 0xFF,
901 (dev_t) ip->i_zone[0] >> MINOR & 0xFF);
902 break;
903 default: printf("%7ld ", ip->i_size);
904 }
905 printpath(0, 1);
906}
907
908/* Remove an entry from a directory if ok with the user.
909 * Don't name the function remove() - that is owned by ANSI, and chaos results
910 * when it is a macro.
911 */
912int Remove(dp)
913dir_struct *dp;
914{
915 setbit(spec_imap, (bit_nr) dp->d_inum);
916 if (yes(". remove entry")) {
917 count[dp->d_inum]--;
918 memset((void *) dp, 0, sizeof(dir_struct));
919 return(1);
920 }
921 return(0);
922}
923
924/* Convert string so that embedded control characters are printable. */
925void make_printable_name(dst, src, n)
926register char *dst;
927register char *src;
928register int n;
929{
930 register int c;
931
932 while (--n >= 0 && (c = *src++) != '\0') {
933 if (isprint(c) && c != '\\')
934 *dst++ = c;
935 else {
936 *dst++ = '\\';
937 switch (c) {
938 case '\\':
939 *dst++ = '\\'; break;
940 case '\b':
941 *dst++ = 'b'; break;
942 case '\f':
943 *dst++ = 'f'; break;
944 case '\n':
945 *dst++ = 'n'; break;
946 case '\r':
947 *dst++ = 'r'; break;
948 case '\t':
949 *dst++ = 't'; break;
950 default:
951 *dst++ = '0' + ((c >> 6) & 03);
952 *dst++ = '0' + ((c >> 3) & 07);
953 *dst++ = '0' + (c & 07);
954 }
955 }
956 }
957 *dst = '\0';
958}
959
960/* See if the `.' or `..' entry is as expected. */
961int chkdots(ino, pos, dp, exp)
962ino_t ino, exp;
963off_t pos;
964dir_struct *dp;
965{
966 char printable_name[4 * NAME_MAX + 1];
967
968 if (dp->d_inum != exp) {
969 make_printable_name(printable_name, dp->d_name, sizeof(dp->d_name));
970 printf("bad %s in ", printable_name);
971 printpath(1, 0);
972 printf("%s is linked to %u ", printable_name, dp->d_inum);
973 printf("instead of %u)", exp);
974 setbit(spec_imap, (bit_nr) ino);
975 setbit(spec_imap, (bit_nr) dp->d_inum);
976 setbit(spec_imap, (bit_nr) exp);
977 if (yes(". repair")) {
978 count[dp->d_inum]--;
979 dp->d_inum = exp;
980 count[exp]++;
981 return(0);
982 }
983 } else if (pos != (dp->d_name[1] ? DIR_ENTRY_SIZE : 0)) {
984 make_printable_name(printable_name, dp->d_name, sizeof(dp->d_name));
985 printf("warning: %s has offset %ld in ", printable_name, pos);
986 printpath(1, 0);
987 printf("%s is linked to %u)\n", printable_name, dp->d_inum);
988 setbit(spec_imap, (bit_nr) ino);
989 setbit(spec_imap, (bit_nr) dp->d_inum);
990 setbit(spec_imap, (bit_nr) exp);
991 }
992 return(1);
993}
994
995/* Check the name in a directory entry. */
996int chkname(ino, dp)
997ino_t ino;
998dir_struct *dp;
999{
1000 register n = NAME_MAX + 1;
1001 register char *p = dp->d_name;
1002
1003 if (*p == '\0') {
1004 printf("null name found in ");
1005 printpath(0, 0);
1006 setbit(spec_imap, (bit_nr) ino);
1007 if (Remove(dp)) return(0);
1008 }
1009 while (*p != '\0' && --n != 0)
1010 if (*p++ == '/') {
1011 printf("found a '/' in entry of directory ");
1012 printpath(1, 0);
1013 setbit(spec_imap, (bit_nr) ino);
1014 printf("entry = '");
1015 printname(dp->d_name);
1016 printf("')");
1017 if (Remove(dp)) return(0);
1018 break;
1019 }
1020 return(1);
1021}
1022
1023/* Check a directory entry. Here the routine `descendtree' is called
1024 * recursively to check the file or directory pointed to by the entry.
1025 */
1026int chkentry(ino, pos, dp)
1027ino_t ino;
1028off_t pos;
1029dir_struct *dp;
1030{
1031 if (dp->d_inum < ROOT_INODE || dp->d_inum > sb.s_ninodes) {
1032 printf("bad inode found in directory ");
1033 printpath(1, 0);
1034 printf("ino found = %u, ", dp->d_inum);
1035 printf("name = '");
1036 printname(dp->d_name);
1037 printf("')");
1038 if (yes(". remove entry")) {
1039 memset((void *) dp, 0, sizeof(dir_struct));
1040 return(0);
1041 }
1042 return(1);
1043 }
1044 if ((unsigned) count[dp->d_inum] == SHRT_MAX) {
1045 printf("too many links to ino %u\n", dp->d_inum);
1046 printf("discovered at entry '");
1047 printname(dp->d_name);
1048 printf("' in directory ");
1049 printpath(0, 1);
1050 if (Remove(dp)) return(0);
1051 }
1052 count[dp->d_inum]++;
1053 if (strcmp(dp->d_name, ".") == 0) {
1054 ftop->st_presence |= DOT;
1055 return(chkdots(ino, pos, dp, ino));
1056 }
1057 if (strcmp(dp->d_name, "..") == 0) {
1058 ftop->st_presence |= DOTDOT;
1059 return(chkdots(ino, pos, dp, ino == ROOT_INODE ? ino :
1060 ftop->st_next->st_dir->d_inum));
1061 }
1062 if (!chkname(ino, dp)) return(0);
1063 if (bitset(dirmap, (bit_nr) dp->d_inum)) {
1064 printf("link to directory discovered in ");
1065 printpath(1, 0);
1066 printf("name = '");
1067 printname(dp->d_name);
1068 printf("', dir ino = %u)", dp->d_inum);
1069 return !Remove(dp);
1070 }
1071 return(descendtree(dp));
1072}
1073
1074/* Check a zone of a directory by checking all the entries in the zone.
1075 * The zone is split up into chunks to not allocate too much stack.
1076 */
1077int chkdirzone(ino, ip, pos, zno)
1078ino_t ino;
1079d_inode *ip;
1080off_t pos;
1081zone_nr zno;
1082{
1083 dir_struct dirblk[CDIRECT];
1084 register dir_struct *dp;
1085 register n, dirty;
1086 register long offset = zaddr(zno);
1087 register off_t size = 0;
1088 n = SCALE * (NR_DIR_ENTRIES(block_size) / CDIRECT);
1089
1090 do {
1091 devread(offset, (char *) dirblk, DIRCHUNK);
1092 dirty = 0;
1093 for (dp = dirblk; dp < &dirblk[CDIRECT]; dp++) {
1094 if (dp->d_inum != NO_ENTRY && !chkentry(ino, pos, dp))
1095 dirty = 1;
1096 pos += DIR_ENTRY_SIZE;
1097 if (dp->d_inum != NO_ENTRY) size = pos;
1098 }
1099 if (dirty) devwrite(offset, (char *) dirblk, DIRCHUNK);
1100 offset += DIRCHUNK;
1101 n--;
1102 } while (n > 0);
1103
1104 if (size > ip->i_size) {
1105 printf("size not updated of directory ");
1106 printpath(2, 0);
1107 if (yes(". extend")) {
1108 setbit(spec_imap, (bit_nr) ino);
1109 ip->i_size = size;
1110 devwrite(inoaddr(ino), (char *) ip, INODE_SIZE);
1111 }
1112 }
1113 return(1);
1114}
1115
1116
1117int chksymlinkzone(ino, ip, pos, zno)
1118ino_t ino;
1119d_inode *ip;
1120off_t pos;
1121zone_nr zno;
1122{
1123 long offset;
1124 size_t len;
1125 char target[PATH_MAX+1];
1126
1127 if (ip->i_size > PATH_MAX)
1128 fatal("chksymlinkzone: fsck program inconsistency\n");
1129 offset = zaddr(zno);
1130 devread(offset, target, ip->i_size);
1131 target[ip->i_size]= '\0';
1132 len= strlen(target);
1133 if (len != ip->i_size)
1134 {
1135 printf("bad size in symbolic link (%d instead of %d) ",
1136 ip->i_size, len);
1137 printpath(2, 0);
1138 if (yes(". update")) {
1139 setbit(spec_imap, (bit_nr) ino);
1140 ip->i_size = len;
1141 devwrite(inoaddr(ino), (char *) ip, INODE_SIZE);
1142 }
1143 }
1144 return 1;
1145}
1146
1147/* There is something wrong with the given zone. Print some details. */
1148void errzone(mess, zno, level, pos)
1149char *mess;
1150zone_nr zno;
1151int level;
1152off_t pos;
1153{
1154 printf("%s zone in ", mess);
1155 printpath(1, 0);
1156 printf("zno = %ld, type = ", zno);
1157 switch (level) {
1158 case 0: printf("DATA"); break;
1159 case 1: printf("SINGLE INDIRECT"); break;
1160 case 2: printf("DOUBLE INDIRECT"); break;
1161 default: printf("VERY INDIRECT");
1162 }
1163 printf(", pos = %ld)\n", pos);
1164}
1165
1166/* Found the given zone in the given inode. Check it, and if ok, mark it
1167 * in the zone bitmap.
1168 */
1169int markzone(zno, level, pos)
1170zone_nr zno;
1171int level;
1172off_t pos;
1173{
1174 register bit_nr bit = (bit_nr) zno - FIRST + 1;
1175
1176 ztype[level]++;
1177 if (zno < FIRST || zno >= sb.s_zones) {
1178 errzone("out-of-range", zno, level, pos);
1179 return(0);
1180 }
1181 if (bitset(zmap, bit)) {
1182 setbit(spec_zmap, bit);
1183 errzone("duplicate", zno, level, pos);
1184 return(0);
1185 }
1186 nfreezone--;
1187 if (bitset(spec_zmap, bit)) errzone("found", zno, level, pos);
1188 setbit(zmap, bit);
1189 return(1);
1190}
1191
1192/* Check an indirect zone by checking all of its entries.
1193 * The zone is split up into chunks to not allocate too much stack.
1194 */
1195int chkindzone(ino, ip, pos, zno, level)
1196ino_t ino;
1197d_inode *ip;
1198off_t *pos;
1199zone_nr zno;
1200int level;
1201{
1202 zone_nr indirect[CINDIR];
1203 register n = NR_INDIRECTS / CINDIR;
1204 register long offset = zaddr(zno);
1205
1206 do {
1207 devread(offset, (char *) indirect, INDCHUNK);
1208 if (!chkzones(ino, ip, pos, indirect, CINDIR, level - 1)) return(0);
1209 offset += INDCHUNK;
1210 } while (--n && *pos < ip->i_size);
1211 return(1);
1212}
1213
1214/* Return the size of a gap in the file, represented by a null zone number
1215 * at some level of indirection.
1216 */
1217off_t jump(level)
1218int level;
1219{
1220 off_t power = ZONE_SIZE;
1221
1222 if (level != 0) do
1223 power *= NR_INDIRECTS;
1224 while (--level);
1225 return(power);
1226}
1227
1228/* Check a zone, which may be either a normal data zone, a directory zone,
1229 * or an indirect zone.
1230 */
1231int zonechk(ino, ip, pos, zno, level)
1232ino_t ino;
1233d_inode *ip;
1234off_t *pos;
1235zone_nr zno;
1236int level;
1237{
1238 if (level == 0) {
1239 if ((ip->i_mode & I_TYPE) == I_DIRECTORY &&
1240 !chkdirzone(ino, ip, *pos, zno))
1241 return(0);
1242 if ((ip->i_mode & I_TYPE) == I_SYMBOLIC_LINK &&
1243 !chksymlinkzone(ino, ip, *pos, zno))
1244 return(0);
1245 *pos += ZONE_SIZE;
1246 return(1);
1247 } else
1248 return chkindzone(ino, ip, pos, zno, level);
1249}
1250
1251/* Check a list of zones given by `zlist'. */
1252int chkzones(ino, ip, pos, zlist, len, level)
1253ino_t ino;
1254d_inode *ip;
1255off_t *pos;
1256zone_nr *zlist;
1257int len;
1258int level;
1259{
1260 register ok = 1, i;
1261
1262 /* The check on the position in the next loop is commented out, since FS
1263 * now requires valid zone numbers in each level that is necessary and FS
1264 * always deleted all the zones in the double indirect block.
1265 */
1266 for (i = 0; i < len /* && *pos < ip->i_size */ ; i++)
1267 if (zlist[i] == NO_ZONE)
1268 *pos += jump(level);
1269 else if (!markzone(zlist[i], level, *pos)) {
1270 *pos += jump(level);
1271 ok = 0;
1272 } else if (!zonechk(ino, ip, pos, zlist[i], level))
1273 ok = 0;
1274 return(ok);
1275}
1276
1277/* Check a file or a directory. */
1278int chkfile(ino, ip)
1279ino_t ino;
1280d_inode *ip;
1281{
1282 register ok, i, level;
1283 off_t pos = 0;
1284
1285 ok = chkzones(ino, ip, &pos, &ip->i_zone[0], NR_DZONE_NUM, 0);
1286 for (i = NR_DZONE_NUM, level = 1; i < NR_ZONE_NUMS; i++, level++)
1287 ok &= chkzones(ino, ip, &pos, &ip->i_zone[i], 1, level);
1288 return(ok);
1289}
1290
1291/* Check a directory by checking the contents. Check if . and .. are present. */
1292int chkdirectory(ino, ip)
1293ino_t ino;
1294d_inode *ip;
1295{
1296 register ok;
1297
1298 setbit(dirmap, (bit_nr) ino);
1299 ok = chkfile(ino, ip);
1300 if (!(ftop->st_presence & DOT)) {
1301 printf(". missing in ");
1302 printpath(2, 1);
1303 ok = 0;
1304 }
1305 if (!(ftop->st_presence & DOTDOT)) {
1306 printf(".. missing in ");
1307 printpath(2, 1);
1308 ok = 0;
1309 }
1310 return(ok);
1311}
1312
1313#ifdef I_SYMBOLIC_LINK
1314
1315/* Check the validity of a symbolic link. */
1316int chklink(ino, ip)
1317ino_t ino;
1318d_inode *ip;
1319{
1320 int ok;
1321
1322 ok = chkfile(ino, ip);
1323 if (ip->i_size <= 0 || ip->i_size > block_size) {
1324 if (ip->i_size == 0)
1325 printf("empty symbolic link ");
1326 else
1327 printf("symbolic link too large (size %ld) ", ip->i_size);
1328 printpath(2, 1);
1329 ok = 0;
1330 }
1331 return(ok);
1332}
1333
1334#endif
1335
1336/* Check the validity of a special file. */
1337int chkspecial(ino, ip)
1338ino_t ino;
1339d_inode *ip;
1340{
1341 int i, ok;
1342
1343 ok = 1;
1344 if ((dev_t) ip->i_zone[0] == NO_DEV) {
1345 printf("illegal device number %ld for special file ", ip->i_zone[0]);
1346 printpath(2, 1);
1347 ok = 0;
1348 }
1349
1350 /* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if
1351 * they are nonzero, since this should not happen.
1352 */
1353 for (i = 1; i < NR_ZONE_NUMS; i++)
1354 if (ip->i_zone[i] != NO_ZONE) {
1355 printf("nonzero zone number %ld for special file ",
1356 ip->i_zone[i]);
1357 printpath(2, 1);
1358 ok = 0;
1359 }
1360 return(ok);
1361}
1362
1363/* Check the mode and contents of an inode. */
1364int chkmode(ino, ip)
1365ino_t ino;
1366d_inode *ip;
1367{
1368 switch (ip->i_mode & I_TYPE) {
1369 case I_REGULAR:
1370 nregular++;
1371 return chkfile(ino, ip);
1372 case I_DIRECTORY:
1373 ndirectory++;
1374 return chkdirectory(ino, ip);
1375 case I_BLOCK_SPECIAL:
1376 nblkspec++;
1377 return chkspecial(ino, ip);
1378 case I_CHAR_SPECIAL:
1379 ncharspec++;
1380 return chkspecial(ino, ip);
1381 case I_NAMED_PIPE:
1382 npipe++;
1383 return chkfile(ino, ip);
1384#ifdef I_SYMBOLIC_LINK
1385 case I_SYMBOLIC_LINK:
1386 nsyml++;
1387 return chklink(ino, ip);
1388#endif
1389 default:
1390 nbadinode++;
1391 printf("bad mode of ");
1392 printpath(1, 0);
1393 printf("mode = %o)", ip->i_mode);
1394 return(0);
1395 }
1396}
1397
1398/* Check an inode. */
1399int chkinode(ino, ip)
1400ino_t ino;
1401d_inode *ip;
1402{
1403 if (ino == ROOT_INODE && (ip->i_mode & I_TYPE) != I_DIRECTORY) {
1404 printf("root inode is not a directory ");
1405 printf("(ino = %u, mode = %o)\n", ino, ip->i_mode);
1406 fatal("");
1407 }
1408 if (ip->i_nlinks == 0) {
1409 printf("link count zero of ");
1410 printpath(2, 0);
1411 return(0);
1412 }
1413 nfreeinode--;
1414 setbit(imap, (bit_nr) ino);
1415 if ((unsigned) ip->i_nlinks > SHRT_MAX) {
1416 printf("link count too big in ");
1417 printpath(1, 0);
1418 printf("cnt = %u)\n", (unsigned) ip->i_nlinks);
1419 count[ino] -= SHRT_MAX;
1420 setbit(spec_imap, (bit_nr) ino);
1421 } else {
1422 count[ino] -= (unsigned) ip->i_nlinks;
1423 }
1424 return chkmode(ino, ip);
1425}
1426
1427/* Check the directory entry pointed to by dp, by checking the inode. */
1428int descendtree(dp)
1429dir_struct *dp;
1430{
1431 d_inode inode;
1432 register ino_t ino = dp->d_inum;
1433 register visited;
1434 struct stack stk;
1435
1436 stk.st_dir = dp;
1437 stk.st_next = ftop;
1438 ftop = &stk;
1439 if (bitset(spec_imap, (bit_nr) ino)) {
1440 printf("found inode %u: ", ino);
1441 printpath(0, 1);
1442 }
1443 visited = bitset(imap, (bit_nr) ino);
1444 if (!visited || listing) {
1445 devread(inoaddr(ino), (char *) &inode, INODE_SIZE);
1446 if (listing) list(ino, &inode);
1447 if (!visited && !chkinode(ino, &inode)) {
1448 setbit(spec_imap, (bit_nr) ino);
1449 if (yes("remove")) {
1450 count[ino] += inode.i_nlinks - 1;
1451 clrbit(imap, (bit_nr) ino);
1452 devwrite(inoaddr(ino), nullbuf, INODE_SIZE);
1453 memset((void *) dp, 0, sizeof(dir_struct));
1454 ftop = ftop->st_next;
1455 return(0);
1456 }
1457 }
1458 }
1459 ftop = ftop->st_next;
1460 return(1);
1461}
1462
1463/* Check the file system tree. */
1464void chktree()
1465{
1466 dir_struct dir;
1467
1468 nfreeinode = sb.s_ninodes;
1469 nfreezone = N_DATA;
1470 dir.d_inum = ROOT_INODE;
1471 dir.d_name[0] = 0;
1472 if (!descendtree(&dir)) fatal("bad root inode");
1473 putchar('\n');
1474}
1475
1476/* Print the totals of all the objects found. */
1477void printtotal()
1478{
1479 printf("blocksize = %5d ", block_size);
1480 printf("zonesize = %5d\n", ZONE_SIZE);
1481 printf("\n");
1482 pr("%8u Regular file%s\n", nregular, "", "s");
1483 pr("%8u Director%s\n", ndirectory, "y", "ies");
1484 pr("%8u Block special file%s\n", nblkspec, "", "s");
1485 pr("%8u Character special file%s\n", ncharspec, "", "s");
1486 if (nbadinode != 0) pr("%6u Bad inode%s\n", nbadinode, "", "s");
1487 pr("%8u Free inode%s\n", nfreeinode, "", "s");
1488 pr("%8u Named pipe%s\n", npipe, "", "s");
1489 pr("%8u Symbolic link%s\n", nsyml, "", "s");
1490/* Don't print some fields.
1491 printf("\n");
1492 pr("%8u Data zone%s\n", ztype[0], "", "s");
1493 pr("%8u Single indirect zone%s\n", ztype[1], "", "s");
1494 pr("%8u Double indirect zone%s\n", ztype[2], "", "s");
1495*/
1496 lpr("%8ld Free zone%s\n", nfreezone, "", "s");
1497}
1498
1499/* Check the device which name is given by `f'. The inodes listed by `clist'
1500 * should be listed separately, and the inodes listed by `ilist' and the zones
1501 * listed by `zlist' should be watched for while checking the file system.
1502 */
1503
1504void chkdev(f, clist, ilist, zlist)
1505char *f, **clist, **ilist, **zlist;
1506{
1507 if (automatic) repair = 1;
1508 fsck_device = f;
1509 initvars();
1510
1511 devopen();
1512
1513 getsuper();
1514
1515 if(block_size < _MIN_BLOCK_SIZE || block_size > _MAX_BLOCK_SIZE)
1516 fatal("funny block size");
1517
1518 if(!(rwbuf = malloc(block_size))) fatal("couldn't allocate fs buf (1)");
1519 if(!(nullbuf = malloc(block_size))) fatal("couldn't allocate fs buf (2)");
1520 memset(nullbuf, 0, block_size);
1521
1522 chksuper();
1523
1524 lsi(clist);
1525
1526 getbitmaps();
1527
1528 fillbitmap(spec_imap, (bit_nr) 1, (bit_nr) sb.s_ninodes + 1, ilist);
1529 fillbitmap(spec_zmap, (bit_nr) FIRST, (bit_nr) sb.s_zones, zlist);
1530
1531 getcount();
1532 chktree();
1533 chkmap(zmap, spec_zmap, (bit_nr) FIRST - 1, BLK_ZMAP, N_ZMAP, "zone");
1534 chkcount();
1535 chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode");
1536 chkilist();
1537 printtotal();
1538
1539 putbitmaps();
1540 freecount();
1541 devclose();
1542
1543 if (changed) printf("----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
1544}
1545
1546int main(argc, argv)
1547int argc;
1548char **argv;
1549{
1550 register char **clist = 0, **ilist = 0, **zlist = 0;
1551
1552 register devgiven = 0;
1553 register char *arg;
1554
1555 if ((1 << BITSHIFT) != 8 * sizeof(bitchunk_t)) {
1556 printf("Fsck was compiled with the wrong BITSHIFT!\n");
1557 exit(1);
1558 }
1559
1560 sync();
1561 prog = *argv++;
1562 while ((arg = *argv++) != 0)
1563 if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) {
1564 case 'a': automatic ^= 1; break;
1565 case 'c':
1566 clist = getlist(&argv, "inode");
1567 break;
1568 case 'i':
1569 ilist = getlist(&argv, "inode");
1570 break;
1571 case 'z':
1572 zlist = getlist(&argv, "zone");
1573 break;
1574 case 'r': repair ^= 1; break;
1575 case 'l': listing ^= 1; break;
1576 case 's': listsuper ^= 1; break;
1577 default:
1578 printf("%s: unknown flag '%s'\n", prog, arg);
1579 }
1580 else {
1581 chkdev(arg, clist, ilist, zlist);
1582 clist = 0;
1583 ilist = 0;
1584 zlist = 0;
1585 devgiven = 1;
1586 }
1587 if (!devgiven) {
1588 printf("Usage: fsck [-acilrsz] file\n");
1589 exit(1);
1590 }
1591 return(0);
1592}
Note: See TracBrowser for help on using the repository browser.