/*
 * Copyright 2000-2004 by Hans Reiser, licensing governed by 
 * reiserfsprogs/README
 */

#include "debugreiserfs.h"
#include <time.h>

extern struct reiserfs_fsstat g_stat_info;

int  do_one_ih_corrupt (struct item_head * ih, unsigned int nr_bytes);
int  do_one_ih_random_corrupt (struct item_head * ih);

void do_one_corruption_in_one_block (reiserfs_filsys_t * fs, struct buffer_head * bh, char * corruption_command);
int corrupt_block_header (struct block_head * blkh, unsigned int offset, unsigned int bytes );
void do_one_block_random_corrupt (struct buffer_head * bh);

static int str2int (char * str, int * res)
{
    int val;
    char * tmp;

    val = (int) strtol (str, &tmp, 0);
    if (tmp == str)
		/* could not convert string into a number */
		return 0;
    *res = val;
    return 1;
}

static int get_rand (double min, double max) {

	/* srand (time (0)); */
	int ret;

	ret =  (int)(min + (int)((max - min + 1) * rand() / (RAND_MAX + 1.0)));
	if ((ret < min) || (ret > max))
		die ("get_rand failed: min %d, max %d, returned %d\n", (int)min, (int)max, ret);

	return ret;
}

static void edit_journal_params (struct journal_params * jp)
{
    char * str;
    size_t n;
    int num;

    printf ("Journal parameters:\n");
    printf ("\tDevice: current: %x: new:", get_jp_journal_dev (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_dev (jp, num);

    printf ("\tFirst block: current: %d: new:",
			get_jp_journal_1st_block (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_1st_block (jp, num);

    printf ("\tSize: current: %d: new:", get_jp_journal_size (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_size (jp, num);

    printf ("\tMagic number: current: %d: new:", get_jp_journal_magic (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_magic (jp, num);

    printf ("\tMax transaction size: current: %d: new:",
			get_jp_journal_max_trans_len (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_max_trans_len (jp, num);

    printf ("\tMax batch size: current: %d: new:",
			get_jp_journal_max_batch (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_max_batch (jp, num);

    printf ("\tMax commit age: current: %d: new:",
			get_jp_journal_max_commit_age (jp));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_jp_journal_max_commit_age (jp, num);
}

/* this allows to edit all super block fields */
static void edit_super_block (reiserfs_filsys_t * fs)
{
    char * str;
    size_t n;
    int num;

    str = 0;
    n = 0;

    /* bs_block_count */
    printf ("\tBlock count: current: %u: new:",
			get_sb_block_count (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_block_count (fs->fs_ondisk_sb, num);

    
    /* sb_free_blocks */
    printf ("\tFree block count: current: %u: new:",
			get_sb_free_blocks (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_free_blocks (fs->fs_ondisk_sb, num);

    /* sb_root_block */
    printf ("\tRoot block: current: %u: new:",
			get_sb_root_block (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_root_block (fs->fs_ondisk_sb, num);

    /* sb_journal */
    edit_journal_params (sb_jp (fs->fs_ondisk_sb));
    
    /* sb_blocksize */
    printf ("\tBlocksize: current: %u: new:",
			get_sb_block_size (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_block_size (fs->fs_ondisk_sb, num);

    /* sb_oid_maxsize */
    printf ("\tMax objectid size: current: %u: new:",
			get_sb_oid_maxsize (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_oid_maxsize (fs->fs_ondisk_sb, num);

    /* sb_oid_cursize */
    printf ("\tCur objectid size: current: %u: new:",
			get_sb_oid_cursize (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_oid_cursize (fs->fs_ondisk_sb, num);
	
    /* sb_state */
    printf ("\tUmount state: current: %u: new:",
			get_sb_umount_state (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_umount_state (fs->fs_ondisk_sb, num);

    /* char s_magic [10]; */
    printf ("\tMagic: current: \"%s\": new:", fs->fs_ondisk_sb->s_v1.s_magic);
    getline (&str, &n, stdin);
    if (strcmp (str, "\n"))
		strncpy (fs->fs_ondisk_sb->s_v1.s_magic, str, n > 10 ? 10 : n);
    
    /* __u16 sb_fsck_state; */
    printf ("\tFilesystem state: current: %u: new:", get_sb_fs_state (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_fs_state (fs->fs_ondisk_sb, num);
	
    /* __u32 sb_hash_function_code; */
    printf ("\tHash code: current: %u: new (tea %d, r5 %d, rupasov %d):",
			get_sb_hash_code (fs->fs_ondisk_sb), TEA_HASH, R5_HASH, YURA_HASH);
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_hash_code (fs->fs_ondisk_sb, num);
    
    /* __u16 sb_tree_height; */
    printf ("\tTree height: current: %u: new:",
			get_sb_tree_height (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_tree_height (fs->fs_ondisk_sb, num);
    
    /* __u16 sb_bmap_nr; */
    printf ("\tNumber of bitmaps: current: %u: new:",
			get_sb_bmap_nr (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_bmap_nr (fs->fs_ondisk_sb, num);

    /* __u16 sb_version; */
    printf ("\tFilesystem format: current: %u: new:",
			le16_to_cpu (fs->fs_ondisk_sb->s_v1.sb_version));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_version (fs->fs_ondisk_sb, num);

    /* __u16 sb_reserved_for_journal; */
    printf ("\tSpace reserved for journal: current: %u: new:",
			get_sb_reserved_for_journal (fs->fs_ondisk_sb));
    getline (&str, &n, stdin);
    if (str2int (str, &num))
		set_sb_reserved_for_journal (fs->fs_ondisk_sb, num);

    print_block (stdout, fs, fs->fs_super_bh);
    if (user_confirmed (stderr, "Is this ok ? [N/Yes]: ", "Yes\n")) {
		mark_buffer_dirty (fs->fs_super_bh);
		bwrite (fs->fs_super_bh);
    }
}


static void corrupt_clobber_hash (char * name, struct item_head * ih, 
								  struct reiserfs_de_head * deh)
{
    printf ("\tCorrupting deh_offset of entry \"%s\" of [%u %u]\n", name,
			get_key_dirid (&ih->ih_key), get_key_objectid (&ih->ih_key));
    set_deh_offset (deh, 700);
}


/* this reads list of desired corruptions from stdin and perform the
   corruptions. Format of that list:
   A hash_code
   C name objectid     - 'C'ut entry 'name' from directory item with 'objectid'
   H name objectid     - clobber 'H'hash of entry 'name' of directory 'objectid'
   I item_num pos_in_item  make pos_in_item-th slot of indirect item to point out of device
   O item_num          - destroy item 'O'rder - make 'item_num'-th to have key bigger than 'item_num' + 1-th item
   D item_num          - 'D'elete item_num-th item
   S item_num value    - change file size (item_num-th item must be stat data)
   F item_num value    - change sd_first_direct_byte of stat data
   J item_num objectid
   E name objectid new - change entry's deh_objectid to new
   P                   - print the block
   B offset bytes_to_corrupt - corrupt bytes_to_corrupt bytes in block header,
                               start from offset
*/

void do_corrupt_one_block (reiserfs_filsys_t * fs, char * fline)
{
    struct buffer_head * bh;
    char * line = 0;
    size_t n = 0;
    unsigned long block;

    block = certain_block (fs);
    printf ("block = %lu\n", block);
    if (block == fs->fs_super_bh->b_blocknr) {
		edit_super_block (fs);
		return;
    }

    if (!fs->fs_bitmap2) {
        struct buffer_head * bm_bh;
        unsigned long bm_block;
        
        if (spread_bitmaps (fs))
            bm_block = ( block / (fs->fs_blocksize * 8) ) ? 
				(block / (fs->fs_blocksize * 8)) * (fs->fs_blocksize * 8) : 
			fs->fs_super_bh->b_blocknr + 1;
        else
            bm_block = fs->fs_super_bh->b_blocknr + 1 + (block / (fs->fs_blocksize * 8));
        
        bm_bh = bread (fs->fs_dev, bm_block, fs->fs_blocksize);
        if (bm_bh) {
            if ( misc_test_bit((block % (fs->fs_blocksize * 8)), bm_bh->b_data) )
                fprintf (stderr, "%lu is used in ondisk bitmap\n", block);
            else
				fprintf (stderr, "%lu is free in ondisk bitmap\n", block);
	        
            brelse (bm_bh);
        }
    } else {
        if (reiserfs_bitmap_test_bit (fs->fs_bitmap2, block))
			fprintf (stderr, "%lu is used in ondisk bitmap\n", block);
        else
			fprintf (stderr, "%lu is free in ondisk bitmap\n", block);
    }
    
    /* READ block */
    bh = bread (fs->fs_dev, block, fs->fs_blocksize);
    if (!bh) {
		printf ("corrupt_one_block: bread fialed\n");
		return;
    }

    if (who_is_this (bh->b_data, fs->fs_blocksize) != THE_LEAF) {
		printf ("Can not corrupt not a leaf node\n");
		brelse (bh);
		return;
    }

    printf ("Corrupting block %lu..\n", bh->b_blocknr);

    if (data(fs)->log_file_name) {
		printf ("Log file : %s\n", data(fs)->log_file_name);
    } else {
		printf ("No Log file specified\n");
    }

    if (fline != NULL) {
		do_one_corruption_in_one_block (fs, bh, fline);
		if ((data(fs)->log_file_name) && (data(fs)->log)) {
			fprintf (data(fs)->log, "%lu\n", block);
			fprintf (data(fs)->log, "%s\n", fline);
		}
    } else {
		/* Get list of corruptions from stdin */
        while (getline (&line, &n, stdin) != -1) {
			if ( line [0] == '\n' ) {
				free (line);
				line = 0;
				n = 0;
				break;
			}
			do_one_corruption_in_one_block (fs, bh, line);
			if ((data(fs)->log_file_name) && (data(fs)->log)) {
				fprintf (data(fs)->log, "%lu\n", block);
				fprintf (data(fs)->log, "%s\n", line);
			}
			
			free (line);
			line = 0;
			n = 0;
		}
    }

    printf ("Done\n");
    bwrite(bh);
    brelse (bh);
    return;
}

void do_one_corruption_in_one_block (reiserfs_filsys_t * fs,
									 struct buffer_head * bh,
									 char * corruption_command)
{
    int i, j;
    struct item_head * ih;
    int item_num;
    int item_numbers;
    int bytes_to_corrupt;
    char code, name [100];
    __u32 objectid, new_objectid;
    int value;
    int hash_code;
    unsigned int pos_in_item;
    int type, format;

    printf ("corruption_command  : %s", corruption_command);

    switch (corruption_command [0]) {
	case '#':
	case '\n':
		break;
		
	case '?':
		printf ("A hash_code     - reset hAsh code in super block\n"
				"T item_num type (0, 1, 2, 3) format (0, 1)\n"
				"C name objectid - Cut entry 'name' from directory item with 'objectid'\n"
				"H name objectid - clobber Hash of entry 'name' of directory 'objectid'\n"
				"I item_num pos_in_item  make pos_in_tem-th slot of Indirect item to point out of device\n"
				"O item_num      - destroy item Order - make 'item_num'-th to have key bigger than 'item_num' + 1-th item\n"
				"D item_num      - Delete item_num-th item\n"
				"S item_num value - change file Size (item_num-th item must be stat data)\n"
				"F item_num value - change sd_First_direct_byte of stat data\n"
				"J item_num objectid - set 'obJectid' of 'item_num'-th item\n"
				"E name objectid objectid - set deh_objectid of an entry to objectid\n"
				"N item_numbers bytes_to_corrupt - corrupt bytes_to_corrupt in number of bytes in item_numbers items\n"
				"B offset bytes_to_corrupt - corrupt bytes_to_corrupt in block_header, start corruption from offset\n");
		
	    break;
		
	case 'P':
		print_block (stderr, fs, bh, 3, -1, -1);
		break;
	    
	case 'A':
		/* corrupt hash record in super block */
		if (sscanf (corruption_command, "%c %d\n", &code, &hash_code) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		reiserfs_warning (stderr, "Changing %s to %s\n",
						  code2name (get_sb_hash_code (fs->fs_ondisk_sb)),
						  code2name (hash_code));
		set_sb_hash_code (fs->fs_ondisk_sb, hash_code);
		break;
	    
	case 'C': /* cut entry */
	case 'H': /* make hash wrong */
		if (sscanf (corruption_command, "%c %s %u\n", &code, name, &objectid) != 3) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
	  }
		
		ih = B_N_PITEM_HEAD (bh, 0);
		for (i = 0; i < get_blkh_nr_items (B_BLK_HEAD (bh)); i ++, ih ++) {
			struct reiserfs_de_head * deh;
			/* look for property objectid */
			if (get_key_objectid (&ih->ih_key) != objectid || !is_direntry_ih (ih))
				continue;

			deh = B_I_DEH (bh, ih);
			
			for (j = 0; j < get_ih_entry_count (ih); j ++, deh ++) {
				/* look for proper entry */
				if (name_in_entry_length (ih, deh, j) == (int)strlen (name) &&
					!strncmp (name, name_in_entry (deh, j), strlen (name)))
					break;
			}
			if (j == get_ih_entry_count (ih)) {
				printf ("Can't find entry %s\n", name);
				exit (1);
			}
			switch (code) {
			case 'H' : /* clobber hash */
				corrupt_clobber_hash (name, ih, deh);
				break;

			case 'C' : /* cut entry */
				cut_entry (fs, bh, i, j, 1);
				break;

			default:
				printf ("Unknown command found\n");
			}
		}

		if (!B_IS_IN_TREE (bh)) {
			printf ("NOTE: block is deleted from the tree\n");
			exit (0);
		}
		break;
		
	case 'E': /* set objectid : used to simulate objectid sharing problem */
		if (sscanf (corruption_command, "%c %s %u %d\n", &code, name, &objectid, &new_objectid) != 4) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		ih = B_N_PITEM_HEAD (bh, 0);
		for (i = 0; i < get_blkh_nr_items (B_BLK_HEAD (bh)); i ++, ih ++) {
			struct reiserfs_de_head * deh;
			/* look for property objectid */
			if (get_key_objectid (&ih->ih_key) != objectid || !is_direntry_ih (ih))
				continue;
			deh = B_I_DEH (bh, ih);
			set_deh_objectid (deh, new_objectid);
			break;
		}  
		break;
		
	case 'T': /* set type of item */
	    if (sscanf (corruption_command, "%c %d %d %d\n", &code, &item_num, &type, &format) != 4) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
	    }
	    if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
			printf ("Wrong format \'%c\', wrong item_num \n", corruption_command [0]);
			return;
	    }
		
	    ih = B_N_PITEM_HEAD (bh, item_num);
	    set_ih_key_format (ih, format);
	    set_type (format, &ih->ih_key, type);
		
	    break;
		
	case 'J': /* set objectid : used to simulate objectid sharing problem */
	    if (sscanf (corruption_command, "%c %d %d\n", &code, &item_num, &objectid) != 3) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
	    }
	    if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
			printf ("Wrong format \'%c\', wrong item_num \n", corruption_command [0]);
			return;
	    }
	    ih = B_N_PITEM_HEAD (bh, item_num);
	    set_key_objectid (&ih->ih_key, objectid);
	    break;
		
	case 'I': /* break unformatted node pointer */
	    if (sscanf (corruption_command, "%c %d %u\n", &code, &item_num, &pos_in_item) != 3) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
	    }
	    if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
			printf ("Wrong format \'%c\', wrong item_num \n", corruption_command [0]);
			return;
	    }
	    ih = B_N_PITEM_HEAD (bh, item_num);
	    if (!is_indirect_ih (ih) || pos_in_item >= I_UNFM_NUM (ih)) {
	        reiserfs_warning (stderr, "Not an indirect item or there is "
							  "not so many unfm ptrs in it\n");
			return;
	    }
	    d32_put((__u32 *)B_I_PITEM (bh, ih), pos_in_item, 
		    get_sb_block_count (fs->fs_ondisk_sb) + 100);
	    break;
	    
	case 'D': /* delete item */
	    if (sscanf (corruption_command, "%c %d\n", &code, &item_num) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
	    }
	    if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
			printf ("Wrong format \'%c\', wrong item_num \n", corruption_command [0]);
			return;
	    }
	    delete_item (fs, bh, item_num);
	    break;
		
	case 'O': /* make item out of order */
		{
			struct key * key;
			if (sscanf (corruption_command, "%c %d\n", &code, &item_num) != 2) {
				printf ("Wrong format \'%c\'\n", corruption_command [0]);
				return;
			}
			if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
				printf ("Wrong format \'%c\', wrong item_num \n", corruption_command [0]);
				return;
			}
			
			/* destroy item order */
			
			if (item_num == get_blkh_nr_items (B_BLK_HEAD (bh)) - 1) {
				printf ("can not destroy order\n");
				return;
			}
			ih = B_N_PITEM_HEAD (bh, item_num);
			key = &(ih + 1)->ih_key;
			set_key_dirid (&ih->ih_key, get_key_dirid (key) + 1);
			
			break;
		}  
	case 'S': /* corrupt st_size */
		{
			/* fixme: old stat data only */
			struct stat_data_v1 * sd;
			
			if (sscanf (corruption_command, "%c %d %d\n", &code, &item_num, &value) != 3) {
				printf ("Wrong format \'%c\'\n", corruption_command [0]);
				return;
			}
			if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
				printf ("Wrong format \'%c\', wrong item_num \n", corruption_command [0]);
				return;
			}
			ih = B_N_PITEM_HEAD (bh, item_num);
			
			sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); 
			reiserfs_warning (stderr, "Changing sd_size of %k from %d to %d\n",
							  &ih->ih_key, sd_v1_size(sd), value);
			set_sd_v1_size( sd, value );
			break;
		}
		
    case 'F': /*         st_first_direct_byte */
		{
			/* fixme: old stat data only */
			struct stat_data_v1 * sd;
			
			if (sscanf (corruption_command, "%c %d %d\n", &code, &item_num, &value) != 3) {
				printf ("Wrong format \'%c\'\n", corruption_command [0]);
				return;
			}
			if (item_num > get_blkh_nr_items (B_BLK_HEAD (bh))) {
				printf ("Wrong format \'%c\', wrong item_num \n",
						corruption_command [0]);
				return;
			}
			ih = B_N_PITEM_HEAD (bh, item_num);
			
			sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); 
			reiserfs_warning (stderr, "Changing sd_first_direct_byte of %k from %d to %d\n",
							  &ih->ih_key, sd_v1_first_direct_byte(sd), value);		
			set_sd_v1_first_direct_byte( sd, value );
			break;
		}
		
	case 'N': /* corrupt N number of items */
	    if (sscanf (corruption_command, "%c %d %d\n",
					&code, &item_numbers, &bytes_to_corrupt) != 3) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
	    }
		
	    if (item_numbers > get_blkh_nr_items (B_BLK_HEAD (bh))) {
			printf ("Wrong item_numbers %d expected not more then %d\n",
					item_numbers,
					get_blkh_nr_items (B_BLK_HEAD (bh)));
			return;
	    }
		
	    for (i = 0; i < item_numbers; i++) { 
			printf ("Do corruptions :  %d item header; \n", i );
			ih = B_N_PITEM_HEAD (bh, i);
			do_one_ih_corrupt (ih, bytes_to_corrupt);
			printf ("Ok\n");
	    }
		
	    break;
	case 'B' :
		{
			struct block_head * blkh;
			unsigned int offset;
			
			if (sscanf (corruption_command, "%c %d %d\n",
						&code, &offset, &bytes_to_corrupt) != 3) {
				printf ("Wrong format \'%c\'\n", corruption_command [0]);
				return;
			}
			
			blkh = B_BLK_HEAD (bh);
			corrupt_block_header (blkh, offset, bytes_to_corrupt);
			break;
		}
		
	default:
		printf ("Unknown command found\n");
	} 
    mark_buffer_dirty (bh);
    return;
}

/* corrupt first nr_bytes bytes in item header */
int do_one_ih_corrupt (struct item_head * ih, unsigned int nr_bytes)
{
	if (nr_bytes > IH_SIZE) {
		printf ("Bad byte number %u expected not more then %lu\n", nr_bytes,
				(unsigned long)IH_SIZE);
		exit (1);
	}

	if (memset ((char *)ih, 0, nr_bytes) != ih ) {
		perror ("do_one_ih_corrupt: memset failed");
		exit (1);
	}

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "\tfirst %u bytes corrupted\n", nr_bytes);

	printf ("\tfirst %u bytes corrupted\n", nr_bytes);
	return 0;
}

/* item header random corruption */
int do_one_ih_random_corrupt (struct item_head * ih)
{
	unsigned int i;
	unsigned int from;
	unsigned int count;
		
	from = get_rand (0,  IH_SIZE - 1);
	count = get_rand (1, IH_SIZE);

	if (from + count > IH_SIZE)
		count = IH_SIZE - from;

	for (i = from; i < from + count; i ++)
		((char *)ih)[i] = get_rand(0, 255);

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "\tfrom %u ( %u )\n", from, count);

	printf ("\tfrom %u ( %u )\n",  from, count);
	return 0;
}

/* Corrupt n bytes in block header */
int corrupt_block_header (struct block_head * blkh, unsigned int offset, 
			  unsigned int bytes) 
{
	if ((offset + bytes) > BLKH_SIZE) {
		printf ("Bad offset number: %u or bad bytes number: %u, the suumary "
			"value expected not more then %lu\n", offset, bytes, 
			(unsigned long)BLKH_SIZE);
		exit (1);
	} 

	if (memset ((char *)blkh, 0, bytes) != blkh ) {
		perror ("corrupt_block_head: memset failed");
		exit (1);   
	}

	printf ("offset : %u, corrupt %u bytes\n", offset, bytes);
	return 0;
}

/* corrupt random number of bytes within block header started from random
   offset */
void do_one_blkh_random_corrupt (struct buffer_head * bh)
{
	struct block_head * blkh;
	unsigned int from;
	unsigned int count;
	unsigned int i;

	from = get_rand (0, BLKH_SIZE - 1);
	count = get_rand (1, BLKH_SIZE);

	blkh = B_BLK_HEAD (bh);

	if (from + count > BLKH_SIZE)
		count = BLKH_SIZE - from;

	for (i = from; i < from + count; i ++)
		((char *)blkh)[i] = get_rand(0, 255);

	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "# : %lu #    ", bh->b_blocknr);
		fprintf (data(fs)->log, "from %u (%u)\n", from, count);
	}

	printf ("# : %lu #   ", bh->b_blocknr);
	printf ("from %u (%u)\n", from, count);

}

void do_leaves_corruption (reiserfs_filsys_t * fs,
						   unsigned long nr_leaves_cr) 
						   
{
	struct buffer_head * bh;
    unsigned long nr_leaves = 0;
	unsigned int i, should_be_corrupted;

	srand (time (0));
	printf ("%lu leaves will be corrupted\n", nr_leaves_cr);
	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "Block headers in %lu leaves will be corrupted\n", nr_leaves_cr);
	}

    if (reiserfs_open_ondisk_bitmap (fs) < 0)
		reiserfs_exit (1, "Could not open ondisk bitmap");

    for (i = 0; (i < get_sb_block_count (fs->fs_ondisk_sb)) &&
			 nr_leaves < nr_leaves_cr; i ++) {

		if (!reiserfs_bitmap_test_bit (fs->fs_bitmap2, i))
			continue;

		bh = bread (fs->fs_dev, i, fs->fs_blocksize);
		if (!bh) {
			reiserfs_warning (stderr, "could not read block %lu\n", i);
			continue;
		}

		if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) {
			brelse (bh);	
			continue;
		}

		if ((!is_leaf_node (bh)) || (block_of_journal (fs, i))) {
			brelse (bh);	
			continue;
		}

		should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
		if (should_be_corrupted == 0)
		{
			brelse (bh);
			continue;
		}
		do_one_blkh_random_corrupt (bh);
		/* do_one_block_random_corrupt (bh); */
		mark_buffer_dirty (bh);
		bwrite(bh);
		brelse (bh);
		nr_leaves ++;
	}

	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log,
				 "%lu leaves WERE corrupted\n",
				 nr_leaves);
	}

	printf ("%lu leaves WERE corrupted\n", nr_leaves);

	reiserfs_close_ondisk_bitmap (fs);
	return;
}

void do_one_block_random_corrupt (struct buffer_head * bh) 
{
	unsigned int from = get_rand (0, bh->b_size - 1);
	unsigned int count = get_rand (1, bh->b_size);
	unsigned int i;

	if (from + count > bh->b_size)
		count = bh->b_size - from;

	for (i = from; i < from + count; i ++)
		((char *)bh->b_data)[i] = get_rand(0, 255);

	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "# block %lu: ", bh->b_blocknr);
		fprintf (data(fs)->log, "from %u ( %u )\n", from, count);
	}

	printf ("# block %lu: ", bh->b_blocknr);
	printf ("from %u ( %u )\n", from, count);
	
}

void do_bitmap_corruption (reiserfs_filsys_t * fs)
{

	unsigned long first = fs->fs_super_bh->b_blocknr + 1;
	unsigned long nr_bitmap_to_corrupt;
	unsigned long block;
	struct buffer_head * bh;
	unsigned int i;

	nr_bitmap_to_corrupt = (unsigned long)get_rand (1, reiserfs_fs_bmap_nr(fs) - 1);

	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "%lu bitmaps will be corrupted\n", nr_bitmap_to_corrupt);
	}
	printf ("%lu bitmaps will be corrupted\n", nr_bitmap_to_corrupt);
	for (i = 0; i < nr_bitmap_to_corrupt; i ++ )
		{
			block = ( i == 0 ) ? first 
				: fs->fs_blocksize * 8 * i;
			bh = bread (fs->fs_dev, block, fs->fs_blocksize);

			if (!bh) {
				printf ("do_bitmap_corruption: bread failed for bitmap %d: %lu\n",
						i, block); 
				exit (1);
			}
			do_one_block_random_corrupt (bh);
			mark_buffer_dirty (bh);
			bwrite(bh);
			brelse (bh);
		}

} 

/* corrupt the random number of item headers in random number of leaves */
void do_ih_random_corrupt (reiserfs_filsys_t * fs,
							  unsigned long nr_leaves_cr) 
{
	unsigned int nr_ih_cr;
	unsigned int i, j;
	struct buffer_head * bh;
	struct item_head * ih;
	unsigned long nr_leaves = 0;
	unsigned int should_be_corrupted = 0;

	srand (time (0));

	printf ("item headers in %lu leaves will be corrupted\n", nr_leaves_cr);
	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "item headers in %lu leaves will be corrupted\n", nr_leaves_cr);
	}

    if (reiserfs_open_ondisk_bitmap (fs) < 0)
		reiserfs_exit (1, "Could not open ondisk bitmap");

    for (i = 0; (i < get_sb_block_count (fs->fs_ondisk_sb)) &&
			 nr_leaves < nr_leaves_cr; i ++) {

		if (!reiserfs_bitmap_test_bit (fs->fs_bitmap2, i))
			continue;

		bh = bread (fs->fs_dev, i, fs->fs_blocksize);
		if (!bh) {
			reiserfs_warning (stderr, "could not read block %lu\n", i);
			continue;
		}

		if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) {
			brelse (bh);	
			continue;
		}

		if ((!is_leaf_node (bh)) || (block_of_journal (fs, i))) {
			brelse (bh);	
			continue;
		}

		should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
		if (should_be_corrupted == 0)
		{
			brelse (bh);
			continue;
		}

		nr_ih_cr = get_rand (1, get_blkh_nr_items (B_BLK_HEAD (bh))); 
		for (j = 0; j < nr_ih_cr; j ++ ) {
			should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
			if (should_be_corrupted == 0)
				continue;

			if ((data(fs)->log_file_name) && (data(fs)->log))
				fprintf (data(fs)->log, "# block %lu , item header %d\n",
						 bh->b_blocknr, j);
			printf ("# block %lu , item header %d\n", bh->b_blocknr, j);
			
			ih = B_N_PITEM_HEAD (bh, j);
			do_one_ih_random_corrupt (ih);
		}
		mark_buffer_dirty (bh);
		bwrite(bh);
		brelse (bh);
		nr_leaves ++ ;
	}

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "item headers in %lu leaves WERE corrupted\n",
				 nr_leaves);
	printf ("item headers in %lu leaves WERE corrupted\n", nr_leaves);

	reiserfs_close_ondisk_bitmap (fs);
}

/* corrupt item */
void do_one_item_random_corrupt (struct buffer_head * bh,
								 struct item_head * ih)
{
	unsigned int i;
	unsigned int from;
	unsigned int count;
	char * p;

	p = (char *)B_I_PITEM(bh, ih);

	from = get_rand (0, get_ih_item_len(ih)  - 1);
	count = get_rand (1, get_ih_item_len(ih));

	if (from + count > get_ih_item_len(ih))
		count = get_ih_item_len(ih) - from;

	for (i = from; i < from + count; i ++)
		((char *)p)[i] = get_rand(0, 255);

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "item body \tfrom %u ( %u )\n", from, count);

	printf ("item body \tfrom %u ( %u )\n",  from, count);
	return ;

}


/* corrupt the random number of directory items in random number of leaves */
void do_dir_random_corrupt (reiserfs_filsys_t * fs,
							unsigned long nr_leaves_cr) 
{
 	unsigned int nr_ih_cr;
	unsigned int i, j;
	struct buffer_head * bh;
	struct item_head * ih;
	unsigned long nr_leaves = 0;
	unsigned int should_be_corrupted = 0;

	srand (time (0));

	printf ("DIR items in %lu leaves will be corrupted\n", nr_leaves_cr);
	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "DIR items in %lu leaves will be corrupted\n", nr_leaves_cr);
	}

    if (reiserfs_open_ondisk_bitmap (fs) < 0)
		reiserfs_exit (1, "Could not open ondisk bitmap");

    for (i = 0; (i < get_sb_block_count (fs->fs_ondisk_sb)) &&
			 nr_leaves < nr_leaves_cr; i ++) {

		if (!reiserfs_bitmap_test_bit (fs->fs_bitmap2, i))
			continue;

		bh = bread (fs->fs_dev, i, fs->fs_blocksize);
		if (!bh) {
			reiserfs_warning (stderr, "could not read block %lu\n", i);
			continue;
		}

		if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) {
			brelse (bh);	
			continue;
		}

		if ((!is_leaf_node (bh)) || (block_of_journal (fs, i))) {
			brelse (bh);	
			continue;
		}

		should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
		if (should_be_corrupted == 0)
		{
			brelse (bh);
			continue;
		}

		/* get next item, look is it a DIR */
		nr_ih_cr = get_rand (1, get_blkh_nr_items (B_BLK_HEAD (bh))); 
		for (j = 0; j < nr_ih_cr; j ++ ) {
			should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
			if (should_be_corrupted == 0)
				continue;

			if ((data(fs)->log_file_name) && (data(fs)->log))
				fprintf (data(fs)->log, "# block %lu , item %d\n",
						 bh->b_blocknr, j);
			printf ("# block %lu , item %d\n", bh->b_blocknr, j);
			
			ih = B_N_PITEM_HEAD (bh, j);
			if (get_type (&ih->ih_key) != TYPE_DIRENTRY)
				continue;
			do_one_item_random_corrupt (bh, ih);
		}
		mark_buffer_dirty (bh);
		bwrite(bh);
		brelse (bh);
		nr_leaves ++ ;
	}

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "DIR items in %lu leaves WERE corrupted\n",
				 nr_leaves);
	printf ("DIR items in %lu leaves WERE corrupted\n", nr_leaves);

	reiserfs_close_ondisk_bitmap (fs);
}

/* corrupt the random number of stat data items in random number of leaves */
void do_sd_random_corrupt (reiserfs_filsys_t * fs,
						   unsigned long nr_leaves_cr) 
{
 	unsigned int nr_ih_cr;
	unsigned int i, j;
	struct buffer_head * bh;
	struct item_head * ih;
	unsigned long nr_leaves = 0;
	unsigned int should_be_corrupted = 0;

	srand (time (0));

	printf ("SD items in %lu leaves will be corrupted\n", nr_leaves_cr);
	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "SD items in %lu leaves will be corrupted\n", nr_leaves_cr);
	}

    if (reiserfs_open_ondisk_bitmap (fs) < 0)
		reiserfs_exit (1, "Could not open ondisk bitmap");

    for (i = 0; (i < get_sb_block_count (fs->fs_ondisk_sb)) &&
			 nr_leaves < nr_leaves_cr; i ++) {

		if (!reiserfs_bitmap_test_bit (fs->fs_bitmap2, i))
			continue;

		bh = bread (fs->fs_dev, i, fs->fs_blocksize);
		if (!bh) {
			reiserfs_warning (stderr, "could not read block %lu\n", i);
			continue;
		}

		if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) {
			brelse (bh);	
			continue;
		}

		if ((!is_leaf_node (bh)) || (block_of_journal (fs, i))) {
			brelse (bh);	
			continue;
		}
		
		should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
		if (should_be_corrupted == 0)
		{
			brelse (bh);
			continue;
		}

		/* get next item, look is it a SD */
		nr_ih_cr = get_rand (1, get_blkh_nr_items (B_BLK_HEAD (bh))); 
		for (j = 0; j < nr_ih_cr; j ++ ) {
			should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
			if (should_be_corrupted == 0)
				continue;

			if ((data(fs)->log_file_name) && (data(fs)->log))
				fprintf (data(fs)->log, "# block %lu , item %d\n",
						 bh->b_blocknr, j);
			printf ("# block %lu , item %d\n", bh->b_blocknr, j);
			
			ih = B_N_PITEM_HEAD (bh, j);
			if (get_type (&ih->ih_key) != TYPE_STAT_DATA)
				continue;
			do_one_item_random_corrupt (bh, ih);
		}
		mark_buffer_dirty (bh);
		bwrite(bh);
		brelse (bh);
		nr_leaves ++ ;
	}

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "SD items in %lu leaves WERE corrupted\n",
				 nr_leaves);
	printf ("SD items in %lu leaves WERE corrupted\n", nr_leaves);

	reiserfs_close_ondisk_bitmap (fs);
}

/* corrupt the random number of indierct items in random number of leaves */
void do_ind_random_corrupt (reiserfs_filsys_t * fs,
						   unsigned long nr_leaves_cr) 
{
 	unsigned int nr_ih_cr;
	unsigned int i, j;
	struct buffer_head * bh;
	struct item_head * ih;
	unsigned long nr_leaves = 0;
	unsigned int should_be_corrupted = 0;

	srand (time (0));

	printf ("IND items in %lu leaves will be corrupted\n", nr_leaves_cr);
	if ((data(fs)->log_file_name) && (data(fs)->log)) {
		fprintf (data(fs)->log, "IND items in %lu leaves will be corrupted\n", nr_leaves_cr);
	}

    if (reiserfs_open_ondisk_bitmap (fs) < 0)
		reiserfs_exit (1, "Could not open ondisk bitmap");

    for (i = 0; (i < get_sb_block_count (fs->fs_ondisk_sb)) &&
			 nr_leaves < nr_leaves_cr; i ++) {

		if (!reiserfs_bitmap_test_bit (fs->fs_bitmap2, i))
			continue;

		bh = bread (fs->fs_dev, i, fs->fs_blocksize);
		if (!bh) {
			reiserfs_warning (stderr, "could not read block %lu\n", i);
			continue;
		}

		if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) {
			brelse (bh);	
			continue;
		}

		if ((!is_leaf_node (bh)) || (block_of_journal (fs, i))) {
			brelse (bh);	
			continue;
		}
		
		should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
		if (should_be_corrupted == 0)
		{
			brelse (bh);
			continue;
		}

		/* get next item, look is it an IND */
		nr_ih_cr = get_rand (1, get_blkh_nr_items (B_BLK_HEAD (bh))); 
		for (j = 0; j < nr_ih_cr; j ++ ) {
			should_be_corrupted = (unsigned int) get_rand ((double)0, (double)1);
			if (should_be_corrupted == 0)
				continue;

			ih = B_N_PITEM_HEAD (bh, j);
			if (get_type (&ih->ih_key) != TYPE_INDIRECT)
				continue;
			if ((data(fs)->log_file_name) && (data(fs)->log))
				fprintf (data(fs)->log, "# block %lu , item %d\n",
						 bh->b_blocknr, j);
			printf ("# block %lu , item %d\n", bh->b_blocknr, j);

			do_one_item_random_corrupt (bh, ih);
		}
		mark_buffer_dirty (bh);
		bwrite(bh);
		brelse (bh);
		nr_leaves ++ ;
	}

	if ((data(fs)->log_file_name) && (data(fs)->log))
		fprintf (data(fs)->log, "IND items in %lu leaves WERE corrupted\n",
				 nr_leaves);
	printf ("IND items in %lu leaves WERE corrupted\n", nr_leaves);

	reiserfs_close_ondisk_bitmap (fs);
}

/* this reads list of desired corruptions from stdin and performs the
   corruptions. Format of that list:
   B           - the random number of bitmap to be corrupted
   L nr_leaves - block headers in nr_leaves leaves to be corupted
   H nr_leaves - the random number of item headers in nr_leaves to be corrupted
   S nr_leaves - the random number of stat data items in nr_leaves to be
                 corrupted
   D nr_leaves - the random number of directory items in nr_leaves to be
                 corrupted
   I nr_leaves - the random number of indirect items in nr_leaves to be
                 corrupted
*/

void what_to_corrupt (reiserfs_filsys_t * fs, char * corruption_command)
{
	unsigned long nr_leaves_cr;
	char code;

	switch (corruption_command [0]){
	case 'B' :
		/* bitmap */
		do_bitmap_corruption (fs);
		break;

	case 'L' :
		/* leaves */
		if (sscanf (corruption_command, "%c %lu\n", &code, &nr_leaves_cr) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		do_leaves_corruption (fs, nr_leaves_cr);		
		break;

	case 'H' :
		/* item headers */
		if (sscanf (corruption_command, "%c %lu\n", &code, &nr_leaves_cr) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		do_ih_random_corrupt (fs, nr_leaves_cr);
		break;

	case 'D' :
		/* directory items */
		if (sscanf (corruption_command, "%c %lu\n", &code, &nr_leaves_cr) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		do_dir_random_corrupt (fs, nr_leaves_cr);
		break;

	case 'S' :
		/* stat data items */
		if (sscanf (corruption_command, "%c %lu\n", &code, &nr_leaves_cr) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		do_sd_random_corrupt (fs, nr_leaves_cr);
		break;

	case 'I' :
		/* indirect items */
		if (sscanf (corruption_command, "%c %lu\n", &code, &nr_leaves_cr) != 2) {
			printf ("Wrong format \'%c\'\n", corruption_command [0]);
			return;
		}
		do_ind_random_corrupt (fs, nr_leaves_cr);
		break;

	default :
		printf ("Unknown command specified\n");
	}
}

void do_fs_random_corrupt (reiserfs_filsys_t * fs)
{
    char * line = 0;
    size_t n = 0;

    printf ("Corrupting fs. Please insert one of the following command\n"
			" B - the random number of bitmap to be corrupted\n"
			" L nr_leaves - block headers in nr_leaves leaves to be corupted\n"
			" H nr_leaves - the random number of item headers in nr_leaves to be corrupted\n"
			" S nr_leaves - the random number of stat data items in nr_leaves to be corrupted\n"
			" D nr_leaves - the random number of directory items in nr_leaves to be corrupted\n"
			" I nr_leaves - the random number of indirect items in nr_leaves to be corrupted\n"
			".. ->\n");

	/* Get list of corruptions from stdin */
	while (getline (&line, &n, stdin) != -1) {
		if ( line [0] == '\n' ) {
			free (line);
			line = 0;
			n = 0;
			break;
		}
		printf ("################## command : %s", line);
		if ((data(fs)->log_file_name) && (data(fs)->log)) {
			fprintf (data(fs)->log, "################## command : %s", line);
		}

		what_to_corrupt (fs, line);
		
		free (line);
		line = 0;
		n = 0;
	}
}

/*
   Local variables:
   c-indentation-style: "K&R"
   mode-name: "LC"
   c-basic-offset: 4
   tab-width: 4
   fill-column: 80
   End:
*/








