minix/commands/fsck.mfs/fsck.c

1667 lines
43 KiB
C
Raw Normal View History

2005-04-21 16:53:53 +02:00
/* Hacks for version 1.6 */
#define INODES_PER_BLOCK V2_INODES_PER_BLOCK(block_size)
#define INODE_SIZE ((int) V2_INODE_SIZE)
#define WORDS_PER_BLOCK (block_size / (int) sizeof(bitchunk_t))
#define MAX_ZONES (V2_NR_DZONES+V2_INDIRECTS(block_size)+(long)V2_INDIRECTS(block_size)*V2_INDIRECTS(block_size))
#define NR_DZONE_NUM V2_NR_DZONES
#define NR_INDIRECTS V2_INDIRECTS(block_size)
#define NR_ZONE_NUMS V2_NR_TZONES
#define ZONE_NUM_SIZE V2_ZONE_NUM_SIZE
#define bit_nr bit_t
#define block_nr block_t
#define d_inode d2_inode
#define d_inum mfs_d_ino
2005-04-21 16:53:53 +02:00
#define dir_struct struct direct
#define i_mode d2_mode
#define i_nlinks d2_nlinks
#define i_size d2_size
#define i_zone d2_zone
#define zone_nr zone_t
/* fsck - file system checker Author: Robbert van Renesse */
/* Modified by Norbert Schlenker
* Removed vestiges of standalone/DOS versions:
* - various unused variables and buffers removed
* - now uses library functions rather than private internal routines
* - bytewise structure copies replaced by structure assignment
* - fixed one bug with 14 character file names
* - other small tweaks for speed
*
* Modified by Lars Fredriksen at the request of Andy Tanenbaum, 90-03-10.
* Removed -m option, by which fsck could be told to make a file
* system on a 360K floppy. The code had limited utility, was buggy,
* and failed due to a bug in the ACK C compiler. Use mkfs instead!
*/
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <minix/config.h>
#include <minix/const.h>
#include <minix/type.h>
#include <minix/u64.h>
#include "mfs/const.h"
#include "mfs/inode.h"
#include "mfs/type.h"
#include "mfs/mfsdir.h"
2005-04-21 16:53:53 +02:00
#include <minix/fslib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <a.out.h>
#include <tools.h>
#include <dirent.h>
#include "exitvalues.h"
2005-04-21 16:53:53 +02:00
#undef N_DATA
unsigned int fs_version = 2, block_size = 0;
#define BITSHIFT 5 /* = log2(#bits(int)) */
2005-04-21 16:53:53 +02:00
#define MAXPRINT 80 /* max. number of error lines in chkmap */
#define CINDIR 128 /* number of indirect zno's read at a time */
#define CDIRECT 1 /* number of dir entries read at a time */
/* Macros for handling bitmaps. Now bit_t is long, these are bulky and the
* type demotions produce a lot of lint. The explicit demotion in POWEROFBIT
* is for efficiency and assumes 2's complement ints. Lint should be clever
* enough not to warn about it since BITMASK is small, but isn't. (It would
* be easier to get right if bit_t was was unsigned (long) since then there
* would be no danger from wierd sign representations. Lint doesn't know
* we only use non-negative bit numbers.) There will usually be an implicit
* demotion when WORDOFBIT is used as an array index. This should be safe
* since memory for bitmaps will run out first.
*/
#define BITMASK ((1 << BITSHIFT) - 1)
#define WORDOFBIT(b) ((b) >> BITSHIFT)
#define POWEROFBIT(b) (1 << ((int) (b) & BITMASK))
#define setbit(w, b) (w[WORDOFBIT(b)] |= POWEROFBIT(b))
#define clrbit(w, b) (w[WORDOFBIT(b)] &= ~POWEROFBIT(b))
#define bitset(w, b) (w[WORDOFBIT(b)] & POWEROFBIT(b))
#define ZONE_CT 360 /* default zones (when making file system) */
#define INODE_CT 95 /* default inodes (when making file system) */
#include "mfs/super.h"
2005-04-21 16:53:53 +02:00
static struct super_block sb;
#define STICKY_BIT 01000 /* not defined anywhere else */
/* Ztob gives the block address of a zone
* btoa64 gives the byte address of a block
2005-04-21 16:53:53 +02:00
*/
#define ztob(z) ((block_nr) (z) << sb.s_log_zone_size)
#define btoa64(b) (mul64u(b, block_size))
2005-04-21 16:53:53 +02:00
#define SCALE ((int) ztob(1)) /* # blocks in a zone */
#define FIRST ((zone_nr) sb.s_firstdatazone) /* as the name says */
/* # blocks of each type */
#define N_IMAP (sb.s_imap_blocks)
#define N_ZMAP (sb.s_zmap_blocks)
#define N_ILIST ((sb.s_ninodes+INODES_PER_BLOCK-1) / INODES_PER_BLOCK)
#define N_DATA (sb.s_zones - FIRST)
/* Block address of each type */
#define OFFSET_SUPER_BLOCK SUPER_BLOCK_BYTES
#define BLK_IMAP 2
#define BLK_ZMAP (BLK_IMAP + N_IMAP)
#define BLK_ILIST (BLK_ZMAP + N_ZMAP)
#define BLK_FIRST ztob(FIRST)
#define ZONE_SIZE ((int) ztob(block_size))
#define NLEVEL (NR_ZONE_NUMS - NR_DZONE_NUM + 1)
/* Byte address of a zone */
2005-04-21 16:53:53 +02:00
#define INDCHUNK ((int) (CINDIR * ZONE_NUM_SIZE))
#define DIRCHUNK ((int) (CDIRECT * DIR_ENTRY_SIZE))
char *prog, *fsck_device; /* program name (fsck), device name */
int firstcnterr; /* is this the first inode ref cnt error? */
bitchunk_t *imap, *spec_imap; /* inode bit maps */
bitchunk_t *zmap, *spec_zmap; /* zone bit maps */
bitchunk_t *dirmap; /* directory (inode) bit map */
char *rwbuf; /* one block buffer cache */
block_nr thisblk; /* block in buffer cache */
char *nullbuf; /* null buffer */
nlink_t *count; /* inode count */
int changed; /* has the diskette been written to? */
struct stack {
dir_struct *st_dir;
struct stack *st_next;
char st_presence;
} *ftop;
int dev; /* file descriptor of the device */
#define DOT 1
#define DOTDOT 2
/* Counters for each type of inode/zone. */
int nfreeinode, nregular, ndirectory, nblkspec, ncharspec, nbadinode;
int nsock, npipe, nsyml, ztype[NLEVEL];
2005-04-21 16:53:53 +02:00
long nfreezone;
int repair, notrepaired = 0, automatic, listing, listsuper; /* flags */
int preen = 0, markdirty = 0;
2005-04-21 16:53:53 +02:00
int firstlist; /* has the listing header been printed? */
unsigned part_offset; /* sector offset for this partition */
char answer[] = "Answer questions with y or n. Then hit RETURN";
int main(int argc, char **argv);
void initvars(void);
void fatal(char *s);
int eoln(int c);
int yes(char *question);
int atoo(char *s);
int input(char *buf, int size);
char *alloc(unsigned nelem, unsigned elsize);
void printname(char *s);
void printrec(struct stack *sp);
void printpath(int mode, int nlcr);
void devopen(void);
void devclose(void);
void devio(block_nr bno, int dir);
void devread(long block, long offset, char *buf, int size);
void devwrite(long block, long offset, char *buf, int size);
void pr(char *fmt, int cnt, char *s, char *p);
void lpr(char *fmt, long cnt, char *s, char *p);
bit_nr getnumber(char *s);
char **getlist(char ***argv, char *type);
void lsuper(void);
#define SUPER_GET 0
#define SUPER_PUT 1
void rw_super(int mode);
void chksuper(void);
void lsi(char **clist);
bitchunk_t *allocbitmap(int nblk);
void loadbitmap(bitchunk_t *bitmap, block_nr bno, int nblk);
void dumpbitmap(bitchunk_t *bitmap, block_nr bno, int nblk);
void fillbitmap(bitchunk_t *bitmap, bit_nr lwb, bit_nr upb, char
**list);
void freebitmap(bitchunk_t *p);
void getbitmaps(void);
void putbitmaps(void);
void chkword(unsigned w1, unsigned w2, bit_nr bit, char *type, int *n,
int *report, bit_t);
void chkmap(bitchunk_t *cmap, bitchunk_t *dmap, bit_nr bit, block_nr
blkno, int nblk, char *type);
void chkilist(void);
void getcount(void);
void counterror(ino_t ino);
void chkcount(void);
void freecount(void);
void printperm(mode_t mode, int shift, int special, int overlay);
void list(ino_t ino, d_inode *ip);
int Remove(dir_struct *dp);
void make_printable_name(char *dst, char *src, int n);
int chkdots(ino_t ino, off_t pos, dir_struct *dp, ino_t exp);
int chkname(ino_t ino, dir_struct *dp);
int chkentry(ino_t ino, off_t pos, dir_struct *dp);
int chkdirzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno);
int chksymlinkzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno);
void errzone(char *mess, zone_nr zno, int level, off_t pos);
int markzone(zone_nr zno, int level, off_t pos);
int chkindzone(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int
level);
off_t jump(int level);
int zonechk(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level);
int chkzones(ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist, int
len, int level);
int chkfile(ino_t ino, d_inode *ip);
int chkdirectory(ino_t ino, d_inode *ip);
int chklink(ino_t ino, d_inode *ip);
int chkspecial(ino_t ino, d_inode *ip);
int chkmode(ino_t ino, d_inode *ip);
int chkinode(ino_t ino, d_inode *ip);
int descendtree(dir_struct *dp);
void chktree(void);
void printtotal(void);
void chkdev(char *f, char **clist, char **ilist, char **zlist);
2005-04-21 16:53:53 +02:00
/* Initialize the variables used by this program. */
void initvars()
{
register level;
nregular = ndirectory = nblkspec = ncharspec =
nbadinode = nsock = npipe = nsyml = 0;
2005-04-21 16:53:53 +02:00
for (level = 0; level < NLEVEL; level++) ztype[level] = 0;
changed = 0;
thisblk = NO_BLOCK;
firstlist = 1;
firstcnterr = 1;
}
/* Print the string `s' and exit. */
void fatal(s)
char *s;
{
printf("%s\nfatal\n", s);
exit(FSCK_EXIT_CHECK_FAILED);
2005-04-21 16:53:53 +02:00
}
/* Test for end of line. */
int eoln(c)
int c;
{
return(c == EOF || c == '\n' || c == '\r');
}
/* Ask a question and get the answer unless automatic is set. */
int yes(question)
char *question;
{
register int c, answerchar;
static int note = 0;
int yes;
2005-04-21 16:53:53 +02:00
if (!repair) {
printf("\n");
return(0);
}
printf("%s? ", question);
if(!note) { printf("(y=yes, n=no, q=quit, A=for yes to all) "); note = 1; }
2005-04-21 16:53:53 +02:00
if (automatic) {
printf("yes\n");
return(1);
}
fflush(stdout);
if ((c = answerchar = getchar()) == 'q' || c == 'Q') exit(FSCK_EXIT_CHECK_FAILED);
if(c == 'A') { automatic = 1; c = 'y'; }
2005-04-21 16:53:53 +02:00
while (!eoln(c)) c = getchar();
yes = !(answerchar == 'n' || answerchar == 'N');
if(!yes) notrepaired = 1;
return yes;
2005-04-21 16:53:53 +02:00
}
/* Convert string to integer. Representation is octal. */
int atoo(s)
register char *s;
{
register int n = 0;
while ('0' <= *s && *s < '8') {
n <<= 3;
n += *s++ - '0';
}
return n;
}
/* If repairing the file system, print a prompt and get a string from user. */
int input(buf, size)
char *buf;
int size;
{
register char *p = buf;
printf("\n");
if (repair) {
printf("--> ");
fflush(stdout);
while (--size) {
*p = getchar();
if (eoln(*p)) {
*p = 0;
return(p > buf);
}
p++;
}
*p = 0;
while (!eoln(getchar()));
return(1);
}
return(0);
}
/* Allocate some memory and zero it. */
char *alloc(nelem, elsize)
unsigned nelem, elsize;
{
char *p;
if ((p = (char *)malloc((size_t)nelem * elsize)) == 0) {
fprintf(stderr, "Tried to allocate %dkB\n",
nelem*elsize/1024);
fatal("out of memory");
}
2005-04-21 16:53:53 +02:00
memset((void *) p, 0, (size_t)nelem * elsize);
return(p);
}
/* Print the name in a directory entry. */
void printname(s)
char *s;
{
register n = MFS_NAME_MAX;
2005-04-21 16:53:53 +02:00
int c;
do {
if ((c = *s) == 0) break;
if (!isprint(c)) c = '?';
putchar(c);
s++;
} while (--n);
}
/* Print the pathname given by a linked list pointed to by `sp'. The
* names are in reverse order.
*/
void printrec(struct stack *sp)
2005-04-21 16:53:53 +02:00
{
if (sp->st_next != 0) {
printrec(sp->st_next);
putchar('/');
printname(sp->st_dir->mfs_d_name);
2005-04-21 16:53:53 +02:00
}
}
/* Print the current pathname. */
void printpath(mode, nlcr)
int mode;
int nlcr;
{
if (ftop->st_next == 0)
putchar('/');
else
printrec(ftop);
switch (mode) {
case 1:
printf(" (ino = %u, ", ftop->st_dir->d_inum);
break;
case 2:
printf(" (ino = %u)", ftop->st_dir->d_inum);
break;
}
if (nlcr) printf("\n");
}
/* Open the device. */
void devopen()
{
if ((dev = open(fsck_device,
(repair || markdirty) ? O_RDWR : O_RDONLY)) < 0) {
2005-04-21 16:53:53 +02:00
perror(fsck_device);
fatal("couldn't open device to fsck");
}
}
/* Close the device. */
void devclose()
{
if (close(dev) != 0) {
perror("close");
fatal("");
}
}
/* Read or write a block. */
void devio(bno, dir)
block_nr bno;
int dir;
{
int r;
2005-04-21 16:53:53 +02:00
if(!block_size) fatal("devio() with unknown block size");
if (dir == READING && bno == thisblk) return;
thisblk = bno;
#if 0
printf("%s at block %5d\n", dir == READING ? "reading " : "writing", bno);
#endif
r= lseek64(dev, btoa64(bno), SEEK_SET, NULL);
if (r != 0)
fatal("lseek64 failed");
2005-04-21 16:53:53 +02:00
if (dir == READING) {
if (read(dev, rwbuf, block_size) == block_size)
return;
} else {
if (write(dev, rwbuf, block_size) == block_size)
return;
}
printf("%s: can't %s block %ld (error = 0x%x)\n", prog,
dir == READING ? "read" : "write", (long) bno, errno);
if (dir == READING) {
printf("Continuing with a zero-filled block.\n");
memset(rwbuf, 0, block_size);
return;
}
fatal("");
}
/* Read `size' bytes from the disk starting at block 'block' and
* byte `offset'.
*/
void devread(block, offset, buf, size)
long block;
2005-04-21 16:53:53 +02:00
long offset;
char *buf;
int size;
{
if(!block_size) fatal("devread() with unknown block size");
if (offset >= block_size)
{
block += offset/block_size;
offset %= block_size;
}
devio(block, READING);
memmove(buf, &rwbuf[offset], size);
2005-04-21 16:53:53 +02:00
}
/* Write `size' bytes to the disk starting at block 'block' and
* byte `offset'.
*/
void devwrite(block, offset, buf, size)
long block;
2005-04-21 16:53:53 +02:00
long offset;
char *buf;
int size;
{
if(!block_size) fatal("devwrite() with unknown block size");
if (!repair) fatal("internal error (devwrite)");
if (offset >= block_size)
{
block += offset/block_size;
offset %= block_size;
}
if (size != block_size) devio(block, READING);
memmove(&rwbuf[offset], buf, size);
devio(block, WRITING);
2005-04-21 16:53:53 +02:00
changed = 1;
}
/* Print a string with either a singular or a plural pronoun. */
void pr(fmt, cnt, s, p)
char *fmt, *s, *p;
int cnt;
{
printf(fmt, cnt, cnt == 1 ? s : p);
}
/* Same as above, but with a long argument */
void lpr(fmt, cnt, s, p)
char *fmt, *s, *p;
long cnt;
{
printf(fmt, cnt, cnt == 1 ? s : p);
}
/* Convert string to number. */
bit_nr getnumber(s)
register char *s;
{
register bit_nr n = 0;
if (s == NULL)
return NO_BIT;
while (isdigit(*s))
n = (n << 1) + (n << 3) + *s++ - '0';
return (*s == '\0') ? n : NO_BIT;
}
/* See if the list pointed to by `argv' contains numbers. */
char **getlist(argv, type)
char ***argv, *type;
{
register char **list = *argv;
register empty = 1;
while (getnumber(**argv) != NO_BIT) {
(*argv)++;
empty = 0;
}
if (empty) {
printf("warning: no %s numbers given\n", type);
return(NULL);
}
return(list);
}
/* Make a listing of the super block. If `repair' is set, ask the user
* for changes.
*/
void lsuper()
{
char buf[80];
do {
/* Most of the following atol's enrage lint, for good reason. */
printf("ninodes = %u", sb.s_ninodes);
if (input(buf, 80)) sb.s_ninodes = atol(buf);
printf("nzones = %ld", sb.s_zones);
if (input(buf, 80)) sb.s_zones = atol(buf);
printf("imap_blocks = %u", sb.s_imap_blocks);
if (input(buf, 80)) sb.s_imap_blocks = atol(buf);
printf("zmap_blocks = %u", sb.s_zmap_blocks);
if (input(buf, 80)) sb.s_zmap_blocks = atol(buf);
printf("firstdatazone = %u", sb.s_firstdatazone_old);
if (input(buf, 80)) sb.s_firstdatazone_old = atol(buf);
2005-04-21 16:53:53 +02:00
printf("log_zone_size = %u", sb.s_log_zone_size);
if (input(buf, 80)) sb.s_log_zone_size = atol(buf);
printf("maxsize = %ld", sb.s_max_size);
if (input(buf, 80)) sb.s_max_size = atol(buf);
printf("block size = %ld", sb.s_block_size);
if (input(buf, 80)) sb.s_block_size = atol(buf);
if (yes("ok now")) {
devwrite(0, OFFSET_SUPER_BLOCK, (char *) &sb, sizeof(sb));
2005-04-21 16:53:53 +02:00
return;
}
printf("flags = ");
if(sb.s_flags & MFSFLAG_CLEAN) printf("CLEAN "); else printf("DIRTY ");
printf("\n");
2005-04-21 16:53:53 +02:00
} while (yes("Do you want to try again"));
if (repair) exit(FSCK_EXIT_OK);
2005-04-21 16:53:53 +02:00
}
/* Get the super block from either disk or user. Do some initial checks. */
void rw_super(int put)
2005-04-21 16:53:53 +02:00
{
if(lseek(dev, OFFSET_SUPER_BLOCK, SEEK_SET) < 0) {
perror("lseek");
fatal("couldn't seek to super block.");
}
if(put == SUPER_PUT) {
if(write(dev, &sb, sizeof(sb)) != sizeof(sb)) {
fatal("couldn't write super block.");
}
return;
}
2005-04-21 16:53:53 +02:00
if(read(dev, &sb, sizeof(sb)) != sizeof(sb)) {
fatal("couldn't read super block.");
}
if (listsuper) lsuper();
if (sb.s_magic == SUPER_MAGIC) fatal("Cannot handle V1 file systems");
if (sb.s_magic == SUPER_V2) {
fs_version = 2;
block_size = /* STATIC_BLOCK_SIZE */ 8192;
} else if(sb.s_magic == SUPER_V3) {
fs_version = 3;
block_size = sb.s_block_size;
} else {
fatal("bad magic number in super block");
}
if (sb.s_ninodes <= 0) fatal("no inodes");
if (sb.s_zones <= 0) fatal("no zones");
if (sb.s_imap_blocks <= 0) fatal("no imap");
if (sb.s_zmap_blocks <= 0) fatal("no zmap");
if (sb.s_firstdatazone != 0 && sb.s_firstdatazone <= 4)
fatal("first data zone too small");
2005-04-21 16:53:53 +02:00
if (sb.s_log_zone_size < 0) fatal("zone size < block size");
if (sb.s_max_size <= 0) {
printf("warning: invalid max file size %ld\n", sb.s_max_size);
sb.s_max_size = LONG_MAX;
}
2005-04-21 16:53:53 +02:00
}
/* Check the super block for reasonable contents. */
void chksuper()
{
register n;
register off_t maxsize;
n = bitmapsize((bit_t) sb.s_ninodes + 1, block_size);
if (sb.s_magic != SUPER_V2 && sb.s_magic != SUPER_V3)
fatal("bad magic number in super block");
if (sb.s_imap_blocks < n) {
printf("need %d bocks for inode bitmap; only have %d\n",
n, sb.s_imap_blocks);
fatal("too few imap blocks");
}
if (sb.s_imap_blocks != n) {
pr("warning: expected %d imap_block%s", n, "", "s");
printf(" instead of %d\n", sb.s_imap_blocks);
}
n = bitmapsize((bit_t) sb.s_zones, block_size);
if (sb.s_zmap_blocks < n) fatal("too few zmap blocks");
if (sb.s_zmap_blocks != n) {
pr("warning: expected %d zmap_block%s", n, "", "s");
printf(" instead of %d\n", sb.s_zmap_blocks);
}
if (sb.s_log_zone_size >= 8 * sizeof(block_nr))
fatal("log_zone_size too large");
if (sb.s_log_zone_size > 8) printf("warning: large log_zone_size (%d)\n",
sb.s_log_zone_size);
sb.s_firstdatazone = (BLK_ILIST + N_ILIST + SCALE - 1) >> sb.s_log_zone_size;
if (sb.s_firstdatazone_old != 0) {
if (sb.s_firstdatazone_old >= sb.s_zones)
fatal("first data zone too large");
if (sb.s_firstdatazone_old < sb.s_firstdatazone)
fatal("first data zone too small");
if (sb.s_firstdatazone_old != sb.s_firstdatazone) {
printf("warning: expected first data zone to be %u ",
sb.s_firstdatazone);
printf("instead of %u\n", sb.s_firstdatazone_old);
sb.s_firstdatazone = sb.s_firstdatazone_old;
}
2005-04-21 16:53:53 +02:00
}
maxsize = MAX_FILE_POS;
if (((maxsize - 1) >> sb.s_log_zone_size) / block_size >= MAX_ZONES)
maxsize = ((long) MAX_ZONES * block_size) << sb.s_log_zone_size;
if(maxsize <= 0)
maxsize = LONG_MAX;
2005-04-21 16:53:53 +02:00
if (sb.s_max_size != maxsize) {
printf("warning: expected max size to be %ld ", maxsize);
printf("instead of %ld\n", sb.s_max_size);
}
if(sb.s_flags & MFSFLAG_MANDATORY_MASK) {
fatal("unsupported feature bits - newer fsck needed");
}
2005-04-21 16:53:53 +02:00
}
int inoblock(int inn)
{
return div64u(mul64u(inn - 1, INODE_SIZE), block_size) + BLK_ILIST;
}
int inooff(int inn)
2005-04-21 16:53:53 +02:00
{
return rem64u(mul64u(inn - 1, INODE_SIZE), block_size);
2005-04-21 16:53:53 +02:00
}
/* Make a listing of the inodes given by `clist'. If `repair' is set, ask
* the user for changes.
*/
void lsi(clist)
char **clist;
{
register bit_nr bit;
register ino_t ino;
d_inode inode, *ip = &inode;
char buf[80];
if (clist == 0) return;
while ((bit = getnumber(*clist++)) != NO_BIT) {
setbit(spec_imap, bit);
ino = bit;
do {
devread(inoblock(ino), inooff(ino), (char *) ip, INODE_SIZE);
2005-04-21 16:53:53 +02:00
printf("inode %u:\n", ino);
printf(" mode = %6o", ip->i_mode);
if (input(buf, 80)) ip->i_mode = atoo(buf);
printf(" nlinks = %6u", ip->i_nlinks);
if (input(buf, 80)) ip->i_nlinks = atol(buf);
printf(" size = %6ld", ip->i_size);
if (input(buf, 80)) ip->i_size = atol(buf);
if (yes("Write this back")) {
devwrite(inoblock(ino), inooff(ino), (char *) ip,
INODE_SIZE);
2005-04-21 16:53:53 +02:00
break;
}
} while (yes("Do you want to change it again"));
}
}
/* Allocate `nblk' blocks worth of bitmap. */
bitchunk_t *allocbitmap(nblk)
int nblk;
{
register bitchunk_t *bitmap;
bitmap = (bitchunk_t *) alloc((unsigned) nblk, block_size);
*bitmap |= 1;
return(bitmap);
}
/* Load the bitmap starting at block `bno' from disk. */
void loadbitmap(bitmap, bno, nblk)
bitchunk_t *bitmap;
block_nr bno;
int nblk;
{
register i;
register bitchunk_t *p;
p = bitmap;
for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
devread(bno, 0, (char *) p, block_size);
2005-04-21 16:53:53 +02:00
*bitmap |= 1;
}
/* Write the bitmap starting at block `bno' to disk. */
void dumpbitmap(bitmap, bno, nblk)
bitchunk_t *bitmap;
block_nr bno;
int nblk;
{
register i;
register bitchunk_t *p = bitmap;
for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK)
devwrite(bno, 0, (char *) p, block_size);
2005-04-21 16:53:53 +02:00
}
/* Set the bits given by `list' in the bitmap. */
void fillbitmap(bitmap, lwb, upb, list)
bitchunk_t *bitmap;
bit_nr lwb, upb;
char **list;
{
register bit_nr bit;
if (list == 0) return;
while ((bit = getnumber(*list++)) != NO_BIT)
if (bit < lwb || bit >= upb) {
if (bitmap == spec_imap)
printf("inode number %ld ", bit);
else
printf("zone number %ld ", bit);
printf("out of range (ignored)\n");
} else
setbit(bitmap, bit - lwb + 1);
}
/* Deallocate the bitmap `p'. */
void freebitmap(p)
bitchunk_t *p;
{
free((char *) p);
}
/* Get all the bitmaps used by this program. */
void getbitmaps()
{
imap = allocbitmap(N_IMAP);
zmap = allocbitmap(N_ZMAP);
spec_imap = allocbitmap(N_IMAP);
spec_zmap = allocbitmap(N_ZMAP);
dirmap = allocbitmap(N_IMAP);
}
/* Release all the space taken by the bitmaps. */
void putbitmaps()
{
freebitmap(imap);
freebitmap(zmap);
freebitmap(spec_imap);
freebitmap(spec_zmap);
freebitmap(dirmap);
}
/* `w1' and `w2' are differing words from two bitmaps that should be
* identical. Print what's the matter with them.
*/
void chkword(w1, w2, bit, type, n, report, phys)
unsigned w1, w2;
char *type;
bit_nr bit;
int *n, *report;
bit_nr phys;
{
for (; (w1 | w2); w1 >>= 1, w2 >>= 1, bit++, phys++)
if ((w1 ^ w2) & 1 && ++(*n) % MAXPRINT == 0 && *report &&
(!repair || automatic || yes("stop this listing")))
*report = 0;
else if (*report)
if ((w1 & 1) && !(w2 & 1))
2007-01-16 17:52:51 +01:00
printf("%s %ld is missing\n", type, bit);
2005-04-21 16:53:53 +02:00
else if (!(w1 & 1) && (w2 & 1))
2007-01-16 17:52:51 +01:00
printf("%s %ld is not free\n", type, bit);
2005-04-21 16:53:53 +02:00
}
/* Check if the given (correct) bitmap is identical with the one that is
* on the disk. If not, ask if the disk should be repaired.
*/
void chkmap(cmap, dmap, bit, blkno, nblk, type)
bitchunk_t *cmap, *dmap;
bit_nr bit;
block_nr blkno;
int nblk;
char *type;
{
register bitchunk_t *p = dmap, *q = cmap;
int report = 1, nerr = 0;
int w = nblk * WORDS_PER_BLOCK;
bit_nr phys = 0;
printf("Checking %s map. ", type);
if(!preen) printf("\n");
fflush(stdout);
2005-04-21 16:53:53 +02:00
loadbitmap(dmap, blkno, nblk);
do {
if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report, phys);
p++;
q++;
bit += 8 * sizeof(bitchunk_t);
phys += 8 * sizeof(bitchunk_t);
} while (--w > 0);
if ((!repair || automatic) && !report) printf("etc. ");
if (nerr > MAXPRINT || nerr > 10) printf("%d errors found. ", nerr);
if (nerr != 0 && yes("install a new map")) dumpbitmap(cmap, blkno, nblk);
if (nerr > 0) printf("\n");
}
/* See if the inodes that aren't allocated are cleared. */
void chkilist()
{
register ino_t ino = 1;
mode_t mode;
printf("Checking inode list. ");
if(!preen) printf("\n");
fflush(stdout);
2005-04-21 16:53:53 +02:00
do
if (!bitset(imap, (bit_nr) ino)) {
devread(inoblock(ino), inooff(ino), (char *) &mode,
sizeof(mode));
2005-04-21 16:53:53 +02:00
if (mode != I_NOT_ALLOC) {
printf("mode inode %u not cleared", ino);
if (yes(". clear")) devwrite(inoblock(ino),
inooff(ino), nullbuf, INODE_SIZE);
2005-04-21 16:53:53 +02:00
}
}
while (++ino <= sb.s_ninodes && ino != 0);
if(!preen) printf("\n");
2005-04-21 16:53:53 +02:00
}
/* Allocate an array to maintain the inode reference counts in. */
void getcount()
{
count = (nlink_t *) alloc((unsigned) (sb.s_ninodes + 1), sizeof(nlink_t));
}
/* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */
void counterror(ino_t ino)
2005-04-21 16:53:53 +02:00
{
d_inode inode;
if (firstcnterr) {
printf("INODE NLINK COUNT\n");
firstcnterr = 0;
}
devread(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
2005-04-21 16:53:53 +02:00
count[ino] += inode.i_nlinks; /* it was already subtracted; add it back */
printf("%5u %5u %5u", ino, (unsigned) inode.i_nlinks, count[ino]);
if (yes(" adjust")) {
if ((inode.i_nlinks = count[ino]) == 0) {
fatal("internal error (counterror)");
inode.i_mode = I_NOT_ALLOC;
clrbit(imap, (bit_nr) ino);
}
devwrite(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
2005-04-21 16:53:53 +02:00
}
}
/* Check if the reference count of the inodes are correct. The array `count'
* is maintained as follows: an entry indexed by the inode number is
* incremented each time a link is found; when the inode is read the link
* count in there is substracted from the corresponding entry in `count'.
* Thus, when the whole file system has been traversed, all the entries
* should be zero.
*/
void chkcount()
{
register ino_t ino;
for (ino = 1; ino <= sb.s_ninodes && ino != 0; ino++)
if (count[ino] != 0) counterror(ino);
if (!firstcnterr) printf("\n");
}
/* Deallocate the `count' array. */
void freecount()
{
free((char *) count);
}
/* Print the inode permission bits given by mode and shift. */
void printperm(mode_t mode, int shift, int special, int overlay)
2005-04-21 16:53:53 +02:00
{
if (mode >> shift & R_BIT)
putchar('r');
else
putchar('-');
if (mode >> shift & W_BIT)
putchar('w');
else
putchar('-');
if (mode & special)
putchar(overlay);
else
if (mode >> shift & X_BIT)
putchar('x');
else
putchar('-');
}
/* List the given inode. */
void list(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
if (firstlist) {
firstlist = 0;
printf(" inode permission link size name\n");
}
printf("%6u ", ino);
switch (ip->i_mode & I_TYPE) {
case I_REGULAR: putchar('-'); break;
case I_DIRECTORY: putchar('d'); break;
case I_CHAR_SPECIAL: putchar('c'); break;
case I_BLOCK_SPECIAL: putchar('b'); break;
case I_NAMED_PIPE: putchar('p'); break;
case I_UNIX_SOCKET: putchar('s'); break;
2005-04-21 16:53:53 +02:00
#ifdef I_SYMBOLIC_LINK
case I_SYMBOLIC_LINK: putchar('l'); break;
#endif
default: putchar('?');
}
printperm(ip->i_mode, 6, I_SET_UID_BIT, 's');
printperm(ip->i_mode, 3, I_SET_GID_BIT, 's');
printperm(ip->i_mode, 0, STICKY_BIT, 't');
printf(" %3u ", ip->i_nlinks);
switch (ip->i_mode & I_TYPE) {
case I_CHAR_SPECIAL:
case I_BLOCK_SPECIAL:
printf(" %2x,%2x ", (dev_t) ip->i_zone[0] >> MAJOR & 0xFF,
(dev_t) ip->i_zone[0] >> MINOR & 0xFF);
break;
default: printf("%7ld ", ip->i_size);
}
printpath(0, 1);
}
/* Remove an entry from a directory if ok with the user.
* Don't name the function remove() - that is owned by ANSI, and chaos results
* when it is a macro.
*/
int Remove(dir_struct *dp)
2005-04-21 16:53:53 +02:00
{
setbit(spec_imap, (bit_nr) dp->d_inum);
if (yes(". remove entry")) {
count[dp->d_inum]--;
memset((void *) dp, 0, sizeof(dir_struct));
return(1);
}
return(0);
}
/* Convert string so that embedded control characters are printable. */
void make_printable_name(dst, src, n)
register char *dst;
register char *src;
register int n;
{
register int c;
while (--n >= 0 && (c = *src++) != '\0') {
if (isprint(c) && c != '\\')
*dst++ = c;
else {
*dst++ = '\\';
switch (c) {
case '\\':
*dst++ = '\\'; break;
case '\b':
*dst++ = 'b'; break;
case '\f':
*dst++ = 'f'; break;
case '\n':
*dst++ = 'n'; break;
case '\r':
*dst++ = 'r'; break;
case '\t':
*dst++ = 't'; break;
default:
*dst++ = '0' + ((c >> 6) & 03);
*dst++ = '0' + ((c >> 3) & 07);
*dst++ = '0' + (c & 07);
}
}
}
*dst = '\0';
}
/* See if the `.' or `..' entry is as expected. */
int chkdots(ino_t ino, off_t pos, dir_struct *dp, ino_t exp)
2005-04-21 16:53:53 +02:00
{
char printable_name[4 * MFS_NAME_MAX + 1];
2005-04-21 16:53:53 +02:00
if (dp->d_inum != exp) {
make_printable_name(printable_name, dp->mfs_d_name, sizeof(dp->mfs_d_name));
2005-04-21 16:53:53 +02:00
printf("bad %s in ", printable_name);
printpath(1, 0);
printf("%s is linked to %u ", printable_name, dp->d_inum);
printf("instead of %u)", exp);
setbit(spec_imap, (bit_nr) ino);
setbit(spec_imap, (bit_nr) dp->d_inum);
setbit(spec_imap, (bit_nr) exp);
if (yes(". repair")) {
count[dp->d_inum]--;
dp->d_inum = exp;
count[exp]++;
return(0);
}
} else if (pos != (dp->mfs_d_name[1] ? DIR_ENTRY_SIZE : 0)) {
make_printable_name(printable_name, dp->mfs_d_name, sizeof(dp->mfs_d_name));
2005-04-21 16:53:53 +02:00
printf("warning: %s has offset %ld in ", printable_name, pos);
printpath(1, 0);
printf("%s is linked to %u)\n", printable_name, dp->d_inum);
setbit(spec_imap, (bit_nr) ino);
setbit(spec_imap, (bit_nr) dp->d_inum);
setbit(spec_imap, (bit_nr) exp);
}
return(1);
}
/* Check the name in a directory entry. */
int chkname(ino_t ino, dir_struct *dp)
2005-04-21 16:53:53 +02:00
{
register n = MFS_NAME_MAX + 1;
register char *p = dp->mfs_d_name;
2005-04-21 16:53:53 +02:00
if (*p == '\0') {
printf("null name found in ");
printpath(0, 0);
setbit(spec_imap, (bit_nr) ino);
if (Remove(dp)) return(0);
}
while (*p != '\0' && --n != 0)
if (*p++ == '/') {
printf("found a '/' in entry of directory ");
printpath(1, 0);
setbit(spec_imap, (bit_nr) ino);
printf("entry = '");
printname(dp->mfs_d_name);
2005-04-21 16:53:53 +02:00
printf("')");
if (Remove(dp)) return(0);
break;
}
return(1);
}
/* Check a directory entry. Here the routine `descendtree' is called
* recursively to check the file or directory pointed to by the entry.
*/
int chkentry(ino_t ino, off_t pos, dir_struct *dp)
2005-04-21 16:53:53 +02:00
{
if (dp->d_inum < ROOT_INODE || dp->d_inum > sb.s_ninodes) {
printf("bad inode found in directory ");
printpath(1, 0);
printf("ino found = %u, ", dp->d_inum);
printf("name = '");
printname(dp->mfs_d_name);
2005-04-21 16:53:53 +02:00
printf("')");
if (yes(". remove entry")) {
memset((void *) dp, 0, sizeof(dir_struct));
return(0);
}
return(1);
}
if ((unsigned) count[dp->d_inum] == SHRT_MAX) {
printf("too many links to ino %u\n", dp->d_inum);
printf("discovered at entry '");
printname(dp->mfs_d_name);
2005-04-21 16:53:53 +02:00
printf("' in directory ");
printpath(0, 1);
if (Remove(dp)) return(0);
}
count[dp->d_inum]++;
if (strcmp(dp->mfs_d_name, ".") == 0) {
2005-04-21 16:53:53 +02:00
ftop->st_presence |= DOT;
return(chkdots(ino, pos, dp, ino));
}
if (strcmp(dp->mfs_d_name, "..") == 0) {
2005-04-21 16:53:53 +02:00
ftop->st_presence |= DOTDOT;
return(chkdots(ino, pos, dp, ino == ROOT_INODE ? ino :
ftop->st_next->st_dir->d_inum));
}
if (!chkname(ino, dp)) return(0);
if (bitset(dirmap, (bit_nr) dp->d_inum)) {
printf("link to directory discovered in ");
printpath(1, 0);
printf("name = '");
printname(dp->mfs_d_name);
2005-04-21 16:53:53 +02:00
printf("', dir ino = %u)", dp->d_inum);
return !Remove(dp);
}
return(descendtree(dp));
}
/* Check a zone of a directory by checking all the entries in the zone.
* The zone is split up into chunks to not allocate too much stack.
*/
int chkdirzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno)
2005-04-21 16:53:53 +02:00
{
dir_struct dirblk[CDIRECT];
register dir_struct *dp;
register n, dirty;
long block= ztob(zno);
register long offset = 0;
2005-04-21 16:53:53 +02:00
register off_t size = 0;
n = SCALE * (NR_DIR_ENTRIES(block_size) / CDIRECT);
do {
devread(block, offset, (char *) dirblk, DIRCHUNK);
2005-04-21 16:53:53 +02:00
dirty = 0;
for (dp = dirblk; dp < &dirblk[CDIRECT]; dp++) {
if (dp->d_inum != NO_ENTRY && !chkentry(ino, pos, dp))
dirty = 1;
pos += DIR_ENTRY_SIZE;
if (dp->d_inum != NO_ENTRY) size = pos;
}
if (dirty) devwrite(block, offset, (char *) dirblk, DIRCHUNK);
2005-04-21 16:53:53 +02:00
offset += DIRCHUNK;
n--;
} while (n > 0);
if (size > ip->i_size) {
printf("size not updated of directory ");
printpath(2, 0);
if (yes(". extend")) {
setbit(spec_imap, (bit_nr) ino);
ip->i_size = size;
devwrite(inoblock(ino), inooff(ino), (char *) ip, INODE_SIZE);
2005-04-21 16:53:53 +02:00
}
}
return(1);
}
2006-01-27 13:58:09 +01:00
int chksymlinkzone(ino_t ino, d_inode *ip, off_t pos, zone_nr zno)
2006-01-27 13:58:09 +01:00
{
long block;
2006-01-27 13:58:09 +01:00
size_t len;
char target[PATH_MAX+1];
if (ip->i_size > PATH_MAX)
fatal("chksymlinkzone: fsck program inconsistency\n");
block= ztob(zno);
devread(block, 0, target, ip->i_size);
2006-01-27 13:58:09 +01:00
target[ip->i_size]= '\0';
len= strlen(target);
if (len != ip->i_size)
{
printf("bad size in symbolic link (%d instead of %d) ",
ip->i_size, len);
printpath(2, 0);
if (yes(". update")) {
setbit(spec_imap, (bit_nr) ino);
ip->i_size = len;
devwrite(inoblock(ino), inooff(ino),
(char *) ip, INODE_SIZE);
2006-01-27 13:58:09 +01:00
}
}
return 1;
}
2005-04-21 16:53:53 +02:00
/* There is something wrong with the given zone. Print some details. */
void errzone(mess, zno, level, pos)
char *mess;
zone_nr zno;
int level;
off_t pos;
{
printf("%s zone in ", mess);
printpath(1, 0);
printf("zno = %ld, type = ", zno);
switch (level) {
case 0: printf("DATA"); break;
case 1: printf("SINGLE INDIRECT"); break;
case 2: printf("DOUBLE INDIRECT"); break;
default: printf("VERY INDIRECT");
}
printf(", pos = %ld)\n", pos);
}
/* Found the given zone in the given inode. Check it, and if ok, mark it
* in the zone bitmap.
*/
int markzone(zno, level, pos)
zone_nr zno;
int level;
off_t pos;
{
register bit_nr bit = (bit_nr) zno - FIRST + 1;
ztype[level]++;
if (zno < FIRST || zno >= sb.s_zones) {
errzone("out-of-range", zno, level, pos);
return(0);
}
if (bitset(zmap, bit)) {
setbit(spec_zmap, bit);
errzone("duplicate", zno, level, pos);
return(0);
}
nfreezone--;
if (bitset(spec_zmap, bit)) errzone("found", zno, level, pos);
setbit(zmap, bit);
return(1);
}
/* Check an indirect zone by checking all of its entries.
* The zone is split up into chunks to not allocate too much stack.
*/
int chkindzone(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)
2005-04-21 16:53:53 +02:00
{
zone_nr indirect[CINDIR];
register n = NR_INDIRECTS / CINDIR;
long block= ztob(zno);
register long offset = 0;
2005-04-21 16:53:53 +02:00
do {
devread(block, offset, (char *) indirect, INDCHUNK);
2005-04-21 16:53:53 +02:00
if (!chkzones(ino, ip, pos, indirect, CINDIR, level - 1)) return(0);
offset += INDCHUNK;
} while (--n && *pos < ip->i_size);
return(1);
}
/* Return the size of a gap in the file, represented by a null zone number
* at some level of indirection.
*/
off_t jump(level)
int level;
{
off_t power = ZONE_SIZE;
if (level != 0) do
power *= NR_INDIRECTS;
while (--level);
return(power);
}
/* Check a zone, which may be either a normal data zone, a directory zone,
* or an indirect zone.
*/
int zonechk(ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)
2005-04-21 16:53:53 +02:00
{
if (level == 0) {
if ((ip->i_mode & I_TYPE) == I_DIRECTORY &&
!chkdirzone(ino, ip, *pos, zno))
return(0);
2006-01-27 13:58:09 +01:00
if ((ip->i_mode & I_TYPE) == I_SYMBOLIC_LINK &&
!chksymlinkzone(ino, ip, *pos, zno))
return(0);
2005-04-21 16:53:53 +02:00
*pos += ZONE_SIZE;
return(1);
} else
return chkindzone(ino, ip, pos, zno, level);
}
/* Check a list of zones given by `zlist'. */
int chkzones(ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist,
int len, int level)
2005-04-21 16:53:53 +02:00
{
register ok = 1, i;
/* The check on the position in the next loop is commented out, since FS
* now requires valid zone numbers in each level that is necessary and FS
* always deleted all the zones in the double indirect block.
*/
for (i = 0; i < len /* && *pos < ip->i_size */ ; i++)
if (zlist[i] == NO_ZONE)
*pos += jump(level);
else if (!markzone(zlist[i], level, *pos)) {
*pos += jump(level);
ok = 0;
} else if (!zonechk(ino, ip, pos, zlist[i], level))
ok = 0;
return(ok);
}
/* Check a file or a directory. */
int chkfile(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
register ok, i, level;
off_t pos = 0;
ok = chkzones(ino, ip, &pos, &ip->i_zone[0], NR_DZONE_NUM, 0);
for (i = NR_DZONE_NUM, level = 1; i < NR_ZONE_NUMS; i++, level++)
ok &= chkzones(ino, ip, &pos, &ip->i_zone[i], 1, level);
return(ok);
}
/* Check a directory by checking the contents. Check if . and .. are present. */
int chkdirectory(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
register ok;
setbit(dirmap, (bit_nr) ino);
ok = chkfile(ino, ip);
if (!(ftop->st_presence & DOT)) {
printf(". missing in ");
printpath(2, 1);
ok = 0;
}
if (!(ftop->st_presence & DOTDOT)) {
printf(".. missing in ");
printpath(2, 1);
ok = 0;
}
return(ok);
}
#ifdef I_SYMBOLIC_LINK
/* Check the validity of a symbolic link. */
int chklink(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
int ok;
ok = chkfile(ino, ip);
if (ip->i_size <= 0 || ip->i_size > block_size) {
if (ip->i_size == 0)
printf("empty symbolic link ");
else
printf("symbolic link too large (size %ld) ", ip->i_size);
printpath(2, 1);
ok = 0;
}
return(ok);
}
#endif
/* Check the validity of a special file. */
int chkspecial(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
int i, ok;
ok = 1;
if ((dev_t) ip->i_zone[0] == NO_DEV) {
printf("illegal device number %ld for special file ", ip->i_zone[0]);
printpath(2, 1);
ok = 0;
}
/* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if
* they are nonzero, since this should not happen.
*/
for (i = 1; i < NR_ZONE_NUMS; i++)
if (ip->i_zone[i] != NO_ZONE) {
printf("nonzero zone number %ld for special file ",
ip->i_zone[i]);
printpath(2, 1);
ok = 0;
}
return(ok);
}
/* Check the mode and contents of an inode. */
int chkmode(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
switch (ip->i_mode & I_TYPE) {
case I_REGULAR:
nregular++;
return chkfile(ino, ip);
case I_DIRECTORY:
ndirectory++;
return chkdirectory(ino, ip);
case I_BLOCK_SPECIAL:
nblkspec++;
return chkspecial(ino, ip);
case I_CHAR_SPECIAL:
ncharspec++;
return chkspecial(ino, ip);
case I_NAMED_PIPE:
npipe++;
return chkfile(ino, ip);
case I_UNIX_SOCKET:
nsock++;
return chkfile(ino, ip);
2005-04-21 16:53:53 +02:00
#ifdef I_SYMBOLIC_LINK
case I_SYMBOLIC_LINK:
nsyml++;
return chklink(ino, ip);
#endif
default:
nbadinode++;
printf("bad mode of ");
printpath(1, 0);
printf("mode = %o)", ip->i_mode);
return(0);
}
}
/* Check an inode. */
int chkinode(ino_t ino, d_inode *ip)
2005-04-21 16:53:53 +02:00
{
if (ino == ROOT_INODE && (ip->i_mode & I_TYPE) != I_DIRECTORY) {
printf("root inode is not a directory ");
printf("(ino = %u, mode = %o)\n", ino, ip->i_mode);
fatal("");
}
if (ip->i_nlinks == 0) {
printf("link count zero of ");
printpath(2, 0);
return(0);
}
nfreeinode--;
setbit(imap, (bit_nr) ino);
if ((unsigned) ip->i_nlinks > SHRT_MAX) {
printf("link count too big in ");
printpath(1, 0);
printf("cnt = %u)\n", (unsigned) ip->i_nlinks);
count[ino] -= SHRT_MAX;
setbit(spec_imap, (bit_nr) ino);
} else {
count[ino] -= (unsigned) ip->i_nlinks;
}
return chkmode(ino, ip);
}
/* Check the directory entry pointed to by dp, by checking the inode. */
int descendtree(dp)
dir_struct *dp;
{
d_inode inode;
register ino_t ino = dp->d_inum;
register visited;
struct stack stk;
stk.st_dir = dp;
stk.st_next = ftop;
ftop = &stk;
if (bitset(spec_imap, (bit_nr) ino)) {
printf("found inode %u: ", ino);
printpath(0, 1);
}
visited = bitset(imap, (bit_nr) ino);
if (!visited || listing) {
devread(inoblock(ino), inooff(ino), (char *) &inode, INODE_SIZE);
2005-04-21 16:53:53 +02:00
if (listing) list(ino, &inode);
if (!visited && !chkinode(ino, &inode)) {
setbit(spec_imap, (bit_nr) ino);
if (yes("remove")) {
count[ino] += inode.i_nlinks - 1;
clrbit(imap, (bit_nr) ino);
devwrite(inoblock(ino), inooff(ino),
nullbuf, INODE_SIZE);
2005-04-21 16:53:53 +02:00
memset((void *) dp, 0, sizeof(dir_struct));
ftop = ftop->st_next;
return(0);
}
}
}
ftop = ftop->st_next;
return(1);
}
/* Check the file system tree. */
void chktree()
{
dir_struct dir;
nfreeinode = sb.s_ninodes;
nfreezone = N_DATA;
dir.d_inum = ROOT_INODE;
dir.mfs_d_name[0] = 0;
2005-04-21 16:53:53 +02:00
if (!descendtree(&dir)) fatal("bad root inode");
putchar('\n');
}
/* Print the totals of all the objects found. */
void printtotal()
{
if(preen) {
printf("%d files, %d directories, %d free inodes, %d free zones\n",
nregular, ndirectory, nfreeinode, nfreezone);
return;
}
2005-04-21 16:53:53 +02:00
printf("blocksize = %5d ", block_size);
printf("zonesize = %5d\n", ZONE_SIZE);
printf("\n");
pr("%8u Regular file%s\n", nregular, "", "s");
pr("%8u Director%s\n", ndirectory, "y", "ies");
pr("%8u Block special file%s\n", nblkspec, "", "s");
pr("%8u Character special file%s\n", ncharspec, "", "s");
2005-04-21 16:53:53 +02:00
if (nbadinode != 0) pr("%6u Bad inode%s\n", nbadinode, "", "s");
pr("%8u Free inode%s\n", nfreeinode, "", "s");
pr("%8u Named pipe%s\n", npipe, "", "s");
pr("%8u Unix socket%s\n", nsock, "", "s");
pr("%8u Symbolic link%s\n", nsyml, "", "s");
2005-04-21 16:53:53 +02:00
/* Don't print some fields.
printf("\n");
pr("%8u Data zone%s\n", ztype[0], "", "s");
pr("%8u Single indirect zone%s\n", ztype[1], "", "s");
pr("%8u Double indirect zone%s\n", ztype[2], "", "s");
2005-04-21 16:53:53 +02:00
*/
lpr("%8ld Free zone%s\n", nfreezone, "", "s");
2005-04-21 16:53:53 +02:00
}
/* Check the device which name is given by `f'. The inodes listed by `clist'
* should be listed separately, and the inodes listed by `ilist' and the zones
* listed by `zlist' should be watched for while checking the file system.
*/
void chkdev(f, clist, ilist, zlist)
char *f, **clist, **ilist, **zlist;
{
if (automatic) repair = 1;
fsck_device = f;
initvars();
devopen();
rw_super(SUPER_GET);
2005-04-21 16:53:53 +02:00
if(block_size < _MIN_BLOCK_SIZE)
2005-04-21 16:53:53 +02:00
fatal("funny block size");
if(!(rwbuf = malloc(block_size))) fatal("couldn't allocate fs buf (1)");
if(!(nullbuf = malloc(block_size))) fatal("couldn't allocate fs buf (2)");
memset(nullbuf, 0, block_size);
chksuper();
if(markdirty) {
if(sb.s_flags & MFSFLAG_CLEAN) {
sb.s_flags &= ~MFSFLAG_CLEAN;
rw_super(SUPER_PUT);
printf("\n----- FILE SYSTEM MARKED DIRTY -----\n\n");
} else {
printf("Filesystem is already dirty.\n");
}
}
/* If preening, skip fsck if clean flag is on. */
if(preen) {
if(sb.s_flags & MFSFLAG_CLEAN) {
printf("%s: clean\n", f);
return;
}
printf("%s: dirty, performing fsck\n", f);
}
2005-04-21 16:53:53 +02:00
lsi(clist);
getbitmaps();
fillbitmap(spec_imap, (bit_nr) 1, (bit_nr) sb.s_ninodes + 1, ilist);
fillbitmap(spec_zmap, (bit_nr) FIRST, (bit_nr) sb.s_zones, zlist);
getcount();
chktree();
chkmap(zmap, spec_zmap, (bit_nr) FIRST - 1, BLK_ZMAP, N_ZMAP, "zone");
chkcount();
chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode");
chkilist();
if(preen) printf("\n");
2005-04-21 16:53:53 +02:00
printtotal();
putbitmaps();
freecount();
if (changed) printf("\n----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n");
/* If we were told to repair the FS, and the user never stopped us from
* doing it, and the FS wasn't marked clean, we can mark the FS as clean.
* If we were stopped from repairing, tell user about it.
*/
if(repair && !(sb.s_flags & MFSFLAG_CLEAN)) {
if(notrepaired) {
printf("\n----- FILE SYSTEM STILL DIRTY -----\n\n");
} else {
sync(); /* update FS on disk before clean flag */
sb.s_flags |= MFSFLAG_CLEAN;
rw_super(SUPER_PUT);
printf("\n----- FILE SYSTEM MARKED CLEAN -----\n\n");
}
}
devclose();
2005-04-21 16:53:53 +02:00
}
int main(argc, argv)
int argc;
char **argv;
{
register char **clist = 0, **ilist = 0, **zlist = 0;
int badflag = 0;
2005-04-21 16:53:53 +02:00
register devgiven = 0;
register char *arg;
if ((1 << BITSHIFT) != 8 * sizeof(bitchunk_t)) {
printf("Fsck was compiled with the wrong BITSHIFT!\n");
exit(FSCK_EXIT_CHECK_FAILED);
2005-04-21 16:53:53 +02:00
}
sync();
prog = *argv++;
while ((arg = *argv++) != 0)
if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) {
case 'd': markdirty = 1; break;
case 'p': preen = repair = automatic = 1; break;
case 'y':
2005-04-21 16:53:53 +02:00
case 'a': automatic ^= 1; break;
case 'c':
clist = getlist(&argv, "inode");
break;
case 'i':
ilist = getlist(&argv, "inode");
break;
case 'z':
zlist = getlist(&argv, "zone");
break;
case 'r': repair ^= 1; break;
case 'l': listing ^= 1; break;
case 's': listsuper ^= 1; break;
case 'f': break;
2005-04-21 16:53:53 +02:00
default:
printf("%s: unknown flag '%s'\n", prog, arg);
badflag = 1;
2005-04-21 16:53:53 +02:00
}
else {
chkdev(arg, clist, ilist, zlist);
clist = 0;
ilist = 0;
zlist = 0;
devgiven = 1;
}
if (!devgiven || badflag) {
printf("Usage: fsck [-dyfpacilrsz] file\n");
exit(FSCK_EXIT_USAGE);
2005-04-21 16:53:53 +02:00
}
return(0);
}