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

/*
 * 2000/10/26 - Initial version.
 */

#include "includes.h"
#include <assert.h>


/* create clean bitmap */
reiserfs_bitmap_t * reiserfs_create_bitmap (unsigned int bit_count)
{
    reiserfs_bitmap_t * bm;

    bm = getmem (sizeof (*bm));
    if (!bm)
	return 0;
    bm->bm_bit_size = bit_count;
    bm->bm_byte_size = ((unsigned long long)bit_count + 7) / 8;
    bm->bm_set_bits = 0;
    bm->bm_map = getmem (bm->bm_byte_size);
    if (!bm->bm_map) {
	freemem (bm);
	return 0;
    }

    return bm;
}

/* Expand existing bitmap.  Return non-zero if can't. FIXME: it is
   assumed that bit_count is new number of blocks to be addressed */
int reiserfs_expand_bitmap (reiserfs_bitmap_t * bm, unsigned int bit_count)
{
    unsigned int byte_count = ((bit_count + 7) / 8);
    char * new_map;

    new_map = expandmem (bm->bm_map, bm->bm_byte_size,
			 byte_count - bm->bm_byte_size);

    if (!new_map) {
	return 1;
    }
    
    bm->bm_map = new_map;
    bm->bm_byte_size = byte_count;
    bm->bm_bit_size = bit_count;

    bm->bm_dirty = 1;

    return 0;
}

void reiserfs_shrink_bitmap (reiserfs_bitmap_t * bm, unsigned int bit_count)
{
    unsigned long i;
    
    assert (bm->bm_bit_size >= bit_count);

    bm->bm_byte_size = (bit_count + 7) / 8;
    bm->bm_bit_size = bit_count;
    bm->bm_set_bits = 0;
    
    bm->bm_dirty = 1;
    
    for (i = 0; i < bm->bm_bit_size; i++) {
	if (reiserfs_bitmap_test_bit(bm, i))
	    bm->bm_set_bits++;
    }
}

/* bitmap destructor */
void reiserfs_delete_bitmap (reiserfs_bitmap_t * bm)
{
    freemem(bm->bm_map);
    bm->bm_map = NULL;		/* to not reuse bitmap handle */
    bm->bm_bit_size = 0;
    bm->bm_byte_size = 0;
    freemem(bm);
}


void reiserfs_bitmap_copy (reiserfs_bitmap_t * to, reiserfs_bitmap_t * from)
{
    assert (to->bm_byte_size == from->bm_byte_size);
    memcpy (to->bm_map, from->bm_map, from->bm_byte_size);
    to->bm_bit_size = from->bm_bit_size;
    to->bm_set_bits = from->bm_set_bits;
    to->bm_dirty = 1;
}

int reiserfs_bitmap_compare (reiserfs_bitmap_t * bm1, reiserfs_bitmap_t * bm2)
{
    unsigned long i, diff;
    unsigned long int bytes, bits;

    assert (bm1->bm_byte_size == bm2->bm_byte_size &&
	    bm1->bm_bit_size == bm2->bm_bit_size);

    diff = 0;

    /* compare full bytes */
    bytes = bm1->bm_bit_size / 8;
    bits = bytes * 8;
    if (memcmp (bm1->bm_map, bm2->bm_map, bytes)) {
	for (i = 0; i < bits; i ++)
	    if (reiserfs_bitmap_test_bit(bm1, i) != reiserfs_bitmap_test_bit(bm2, i))
		diff ++;
    }
    
    /* compare last byte of bitmap which can be used partially */
    bits = bm1->bm_bit_size % 8;
    for (i = bm1->bm_bit_size / 8 * 8; i < bm1->bm_bit_size / 8 * 8 + bits; i ++)
	if (reiserfs_bitmap_test_bit(bm1, i) != reiserfs_bitmap_test_bit(bm2, i))
            diff ++;

/*	int mask;

	mask = 255 >> (8 - bits);
	if ((bm1->bm_map [bytes] & mask) != (bm2->bm_map [bytes] & mask)) {
	    diff ++;
	}
    }*/
    return diff;
}

/* 
    Does X | Y for every bit of the bitmap `to`, where 
    X - bit of the `to` bitmap, 
    Y - `from` bitmap. 
    Save result in the `to` bitmap.
*/
void reiserfs_bitmap_disjunction (reiserfs_bitmap_t * to, 
    reiserfs_bitmap_t * from) 
{
    unsigned int i;

    assert (to->bm_byte_size == from->bm_byte_size &&
	    to->bm_bit_size == from->bm_bit_size);

    for (i = 0; i < to->bm_bit_size; i++) {
	if (misc_test_bit(i, from->bm_map) && !misc_test_bit(i, to->bm_map)) {
	    misc_set_bit(i, to->bm_map);
	    to->bm_set_bits ++;
	    to->bm_dirty = 1;	
	}
    }
}

/* 
    Does X & !Y for every bit of the bitmap `base`, where 
    X - bit of the `base` bitmap, 
    Y - `exclude` bitmap. 
    Save result in the `base` bitmap.
*/
void reiserfs_bitmap_delta (reiserfs_bitmap_t * base, reiserfs_bitmap_t * exclude) {
   unsigned int i;

    assert (base->bm_byte_size == exclude->bm_byte_size &&
	    base->bm_bit_size == exclude->bm_bit_size);

    for (i = 0; i < base->bm_bit_size; i++) {
	if (misc_test_bit(i, exclude->bm_map) && misc_test_bit(i, base->bm_map)) {
	    misc_clear_bit(i, base->bm_map);
	    base->bm_set_bits --;
	    base->bm_dirty = 1;
	}
    }
}

void reiserfs_bitmap_set_bit (reiserfs_bitmap_t * bm, unsigned int bit_number)
{
    assert(bit_number < bm->bm_bit_size);
    if (misc_test_bit (bit_number, bm->bm_map))
	return;
    misc_set_bit(bit_number, bm->bm_map);
    bm->bm_set_bits ++;
    bm->bm_dirty = 1;
}


void reiserfs_bitmap_clear_bit (reiserfs_bitmap_t * bm, unsigned int bit_number)
{
    assert(bit_number < bm->bm_bit_size);
    if (!misc_test_bit (bit_number, bm->bm_map))
	return;
    misc_clear_bit (bit_number, bm->bm_map);
    bm->bm_set_bits --;
    bm->bm_dirty = 1;
}


int reiserfs_bitmap_test_bit (reiserfs_bitmap_t * bm, unsigned int bit_number)
{
    if (bit_number >= bm->bm_bit_size)
	printf ("bit %u, bitsize %lu\n", bit_number, bm->bm_bit_size);
    assert(bit_number < bm->bm_bit_size);
    return misc_test_bit(bit_number, bm->bm_map);
}


unsigned int reiserfs_bitmap_zeros (reiserfs_bitmap_t * bm) {
    return bm->bm_bit_size - bm->bm_set_bits;
}


unsigned int reiserfs_bitmap_ones (reiserfs_bitmap_t * bm) {
    return bm->bm_set_bits;
}


int reiserfs_bitmap_find_zero_bit (reiserfs_bitmap_t * bm, unsigned long * first)
{
    unsigned long bit_nr = *first;
    assert(*first < bm->bm_bit_size);

    bit_nr = misc_find_next_zero_bit(bm->bm_map, bm->bm_bit_size, *first);

    if (bit_nr >= bm->bm_bit_size) { /* search failed */	
	return 1;
    }

    *first = bit_nr;
    return 0;
}


/* read every bitmap block and copy their content into bitmap 'bm' */
static int reiserfs_fetch_ondisk_bitmap (reiserfs_bitmap_t * bm, reiserfs_filsys_t * fs)
{
    unsigned int last_byte_unused_bits;
    unsigned long block, to_copy;
    struct buffer_head * bh;
    unsigned int i;
    int copied;
    int ret = 0;
    char * p;

    to_copy = (get_sb_block_count (fs->fs_ondisk_sb) + 7) / 8;

    /*reiserfs_warning (stderr, "Fetching on-disk bitmap..");*/
    assert (bm->bm_byte_size == to_copy);

    copied = fs->fs_blocksize;
    p = bm->bm_map;
    block = fs->fs_super_bh->b_blocknr + 1;

    while (to_copy) {
	bh = bread (fs->fs_dev, block, fs->fs_blocksize);
	if (!bh) {
	    reiserfs_warning (stderr, "reiserfs_fetch_ondisk_bitmap: "
			      "bread failed reading bitmap (%lu)\n", block);
	    
	    bh = getblk (fs->fs_dev, block, fs->fs_blocksize); 
	    if (!bh) {
		reiserfs_exit (1, "reiserfs_fetch_ondisk_bitmap: "
			       "getblk failed");
	    }

	    memset (bh->b_data, 0xff, bh->b_size);
	    mark_buffer_uptodate (bh, 1);
	}

	if (to_copy < fs->fs_blocksize) {
	    for (i = to_copy; i < fs->fs_blocksize; i++) {
		if (bh->b_data[i] != (char)0xff) {
		    ret = 1;
		    break;
		}
	    }
	    
	    copied = to_copy;
	}
	memcpy (p, bh->b_data, copied); 
	brelse (bh);
	p += copied;
	to_copy -= copied;

	/* next bitmap block */
	if (spread_bitmaps (fs))
	    block = (block / (fs->fs_blocksize * 8) + 1) * (fs->fs_blocksize * 8);
	else
	    block ++;
    }

    /* on disk bitmap has bits out of SB_BLOCK_COUNT set to 1, where as
       reiserfs_bitmap_t has those bits set to 0 */
    last_byte_unused_bits = bm->bm_byte_size * 8 - bm->bm_bit_size;

    for (i = 0; i < last_byte_unused_bits; i ++) {
	if (misc_test_bit (bm->bm_bit_size + i, bm->bm_map) == 0)
	    ret = 1;
	else	    
	    misc_clear_bit (bm->bm_bit_size + i, bm->bm_map);
    }

    bm->bm_set_bits = 0;
    /* FIXME: optimize that */
    for (i = 0; i < bm->bm_bit_size; i ++)
	if (reiserfs_bitmap_test_bit (bm, i))
	    bm->bm_set_bits ++;
    
    bm->bm_dirty = 0;

    return ret;
}


/* copy bitmap 'bm' to buffers which hold on-disk bitmap if bitmap was ever
   changed and return 1. Otherwise - return 0 */
int reiserfs_flush_to_ondisk_bitmap (reiserfs_bitmap_t * bm, reiserfs_filsys_t * fs)
{
    unsigned int last_byte_unused_bits, i;
    unsigned long to_copy, copied, block;
    struct buffer_head * bh;
    char * p;

    /* make sure that the device is big enough */
    bh = bread (fs->fs_dev, bm->bm_bit_size - 1, fs->fs_blocksize);
    if (!bh) {
	reiserfs_warning (stderr, "reiserfs_flush_to_ondisk_bitmap: bread failed for block %lu\n",
			  bm->bm_bit_size - 1);
/*
	bh = getblk (fs->fs_dev, bm->bm_bit_size - 1, fs->fs_blocksize);
	if (!bh)
	    reiserfs_panic ("reiserfs_flush_to_ondisk_bitmap: getblk failed");
	mark_buffer_uptodate (bh, 1);
	mark_buffer_dirty (bh);
	bwrite (bh);*/
    }
    brelse (bh);

    if (!bm->bm_dirty)
	return 0;

    to_copy = bm->bm_byte_size;

    copied = fs->fs_blocksize;
    p = bm->bm_map;
    block = fs->fs_super_bh->b_blocknr + 1;

    while (to_copy) {
	/* we bread to make sure that filesystem contains enough blocks */
	bh = getblk (fs->fs_dev, block, fs->fs_blocksize);
	if (!bh) {
	    reiserfs_exit (1, "Getblk failed for (%lu)\n", block);
	}
	
	memset (bh->b_data, 0xff, bh->b_size);
	mark_buffer_uptodate (bh, 1);

	if (to_copy < fs->fs_blocksize)
	    copied = to_copy;
	memcpy (bh->b_data, p, copied);
	if (copied == to_copy) {
	    /* set unused bits of last byte of a bitmap to 1 */
	    last_byte_unused_bits = bm->bm_byte_size * 8 - bm->bm_bit_size;

	    for (i = 0; i < last_byte_unused_bits; i ++)
		misc_set_bit ((bm->bm_bit_size % (fs->fs_blocksize * 8)) + i, bh->b_data);
	}
	mark_buffer_dirty (bh);
	brelse (bh);
	p += copied;
	to_copy -= copied;

	/* next bitmap block */
	if (spread_bitmaps (fs))
	    block = (block / (fs->fs_blocksize * 8) + 1) * (fs->fs_blocksize * 8);
	else
	    block ++;
    }


    return 1;
}


void reiserfs_bitmap_zero (reiserfs_bitmap_t * bm)
{
    memset (bm->bm_map, 0, bm->bm_byte_size);
    bm->bm_set_bits = 0;
    bm->bm_dirty = 1;
}


void reiserfs_bitmap_fill (reiserfs_bitmap_t * bm)
{
    memset (bm->bm_map, 0xff, bm->bm_byte_size);
    bm->bm_set_bits = bm->bm_bit_size;
    bm->bm_dirty = 1;
}


/* format of bitmap saved in a file:
   magic number (32 bits)
   bm_bit_size (32 bits)
   number of ranges of used and free blocks (32 bits)
   number of contiguously used block, .. of free blocks, used, free, etc
   magic number (32 bits) */

#define BITMAP_START_MAGIC 374031
#define BITMAP_END_MAGIC 7786472


FILE * open_file (char * filename, char * option)
{
    FILE * fp = fopen (filename, option);
    if (!fp) {
	reiserfs_warning (stderr, "open_file: could not open file %s\n", filename);
	return 0;
    }
    reiserfs_warning (stderr, "Temp file opened by fsck: \"%s\" .. \n", filename);
    return fp;
}


void close_file (FILE * fp)
{
    fclose (fp);
    /*reiserfs_warning (stderr, "done\n"); fflush (stderr);*/
}


void reiserfs_bitmap_save (FILE * fp, reiserfs_bitmap_t * bm)
{
//    FILE * fp;
    __u32 v;
    int zeros;
    int count;
    unsigned int i;
    int extents;
    long position;

  /*  fp = fopen (filename, "w+");
    if (!fp) {
	reiserfs_warning (stderr, "reiserfs_bitmap_save: could not save bitmap in %s: %s",
			  filename, strerror(errno));
	return;
    }*/


//    reiserfs_warning (stderr, "Saving bitmap in \"%s\" .. ", filename); fflush (stderr);

	

    v = BITMAP_START_MAGIC;
    fwrite (&v, 4, 1, fp);

    v = bm->bm_bit_size;
    fwrite (&v, 4, 1, fp);

    /*printf ("SAVE: bit_size - %d\n", v);*/

    position = ftell(fp);

    if (fseek (fp, 4, SEEK_CUR)) {
	reiserfs_warning (stderr, "reiserfs_bitmap_save: fseek failed: %s\n", strerror(errno));
//	fclose (fp);
	return;
    }

    zeros = 0;
    count = 0;
    extents = 0;
    for (i = 0; i < v; i ++) {
	if (reiserfs_bitmap_test_bit (bm, i)) {
	    if (zeros) {
		/* previous bit was not set, write amount of not set
                   bits, switch to count set bits */
		fwrite (&count, 4, 1, fp);
		/*printf ("SAVE: Free %d\n", count);*/
		extents ++;
		count = 1;
		zeros = 0;
	    } else {
		/* one more zero bit appeared */
		count ++;
	    }
	} else {
	    /* zero bit found */
	    if (zeros) {
		count ++;
	    } else {
		/* previous bit was set, write amount of set bits,
                   switch to count not set bits */
		fwrite (&count, 4, 1, fp);
		/*printf ("SAVE: Used %d\n", count);*/
		extents ++;
		count = 1;
		zeros = 1;
	    }
	}
    }

    fwrite (&count, 4, 1, fp);
    extents ++;
/*
    if (zeros)
	printf ("SAVE: Free %d\n", count);
    else	
	printf ("SAVE: Used %d\n", count);
*/

    v = BITMAP_END_MAGIC;
    fwrite (&v, 4, 1, fp);

    if (fseek (fp, position, SEEK_SET)) {
	reiserfs_warning (stderr, "reiserfs_bitmap_save: fseek failed: %s", strerror(errno));
	return;
    }

    fwrite (&extents, 4, 1, fp);

    if (fseek (fp, 0, SEEK_END)) {
	reiserfs_warning (stderr, "reiserfs_bitmap_save: fseek failed: %s", strerror(errno));
	return;
    }
}


/* format of fsck dump file:
        after pass0
   magic number                 (32 bits)
   passed stage number
   bitmap of leaves
   bitmap of good_unfm
   bitmap of bad_unfm
   magic number                 (32 bits) */

#define FSCK_DUMP_START_MAGIC 374033
#define FSCK_DUMP_END_MAGIC 7786470


void reiserfs_begin_stage_info_save(FILE * file, unsigned long stage)
{
    __u32 v = FSCK_DUMP_START_MAGIC;
    fwrite (&v, 4, 1, file);
    fwrite (&stage, 4, 1, file);
}


void reiserfs_end_stage_info_save(FILE * file)
{
    __u32 v = FSCK_DUMP_END_MAGIC;
    fwrite (&v, 4, 1, file);
}


/*return last passed stage*/
int is_stage_magic_correct (FILE * fp)
{
    __u32 v;

    if (fseek (fp, -4, SEEK_END)) {
	reiserfs_warning (stderr, "is_stage_magic_correct: fseek failed: %s\n", 
	    strerror(errno));
	return -1;
    }

    fread (&v, 4, 1, fp);
    if (v != FSCK_DUMP_END_MAGIC) {
	reiserfs_warning (stderr, "is_stage_magic_correct: no magic found\n");	
	return -1;
    }

    if (fseek (fp, 0, SEEK_SET)) {
	reiserfs_warning (stderr, "is_stage_magic_correct: fseek failed: %s\n", 
	    strerror(errno));
	return -1;
    }

    fread (&v, 4, 1, fp);
    if (v != FSCK_DUMP_START_MAGIC) {
	reiserfs_warning (stderr, "is_stage_magic_correct: no magic found\n");	
	return -1;
    }

    fread (&v, 4, 1, fp);
    if (v != PASS_0_DONE && v != PASS_1_DONE && v != TREE_IS_BUILT && v != SEMANTIC_DONE && v != LOST_FOUND_DONE) {
	reiserfs_warning (stderr, "is_stage_magic_correct: wrong pass found");	
	return -1;
    }

    return (__u16)v;
}


reiserfs_bitmap_t * reiserfs_bitmap_load (FILE * fp)
{
//    FILE * fp;
    __u32 v;
    int count;
    int i, j;
    int extents;
    int bit;
    reiserfs_bitmap_t * bm;
    
/*    fp = fopen (filename, "r");
    if (!fp) {
	reiserfs_warning (stderr, "reiserfs_bitmap_load: fopen failed: %s\n", strerror(errno));
	return 0;
    }*/

    fread (&v, 4, 1, fp);
    if (v != BITMAP_START_MAGIC) {
	reiserfs_warning (stderr, "reiserfs_bitmap_load: "
			  "no bitmap start magic found");	
//	fclose (fp);
	return 0;
    }
	
    /* read bit size of bitmap */
    fread (&v, 4, 1, fp);

    bm = reiserfs_create_bitmap (v);
    if (!bm) {
	reiserfs_warning (stderr, "reiserfs_bitmap_load: creation failed");	
//	fclose (fp);
	return 0;
    }
    
    /*printf ("LOAD: bit_size - %d\n", v);*/

    fread (&extents, 4, 1, fp);

    /*printf ("LOAD: extents - %d\n", extents);*/

    bit = 0;
    for (i = 0; i < extents; i ++) {
	fread (&count, 4, 1, fp);
/*
	if (i % 2)
	    printf ("LOAD: Free %d\n", count);
	else
	    printf ("LOAD: Used %d\n", count);
*/
	for (j = 0; j < count; j ++, bit ++)
	    if (i % 2 == 0) {
		reiserfs_bitmap_set_bit (bm, bit);
	    }
    }

    fread (&v, 4, 1, fp);

    /*printf ("LOAD: Endmagic %d\n", v);*/

//    fclose (fp);
    if (v != BITMAP_END_MAGIC) {
	reiserfs_warning (stderr, "reiserfs_bitmap_load: "
			  "no bitmap end magic found");
	return 0;
    }

    /*    reiserfs_warning (stderr, "%d bits set - done\n", reiserfs_bitmap_ones (bm));*/
    fflush (stderr);
    return bm;
}


void reiserfs_bitmap_invert (reiserfs_bitmap_t * bm)
{
    unsigned int i;

    /*reiserfs_warning (stderr, "Bitmap inverting..");fflush (stderr);*/
    for (i = 0; i < bm->bm_bit_size; i ++) {
	if (reiserfs_bitmap_test_bit (bm, i))
	    reiserfs_bitmap_clear_bit (bm, i);
	else
	    reiserfs_bitmap_set_bit (bm, i);
    }

    /*reiserfs_warning (stderr, "done\n");*/
}


void reiserfs_free_ondisk_bitmap (reiserfs_filsys_t * fs)
{
    if (fs->fs_bitmap2) {
	reiserfs_delete_bitmap (fs->fs_bitmap2);
	fs->fs_bitmap2 = 0;
    }
}

/* read bitmap blocks */
int reiserfs_open_ondisk_bitmap (reiserfs_filsys_t * fs)
{
    unsigned int blocks = get_sb_block_count(fs->fs_ondisk_sb) ;
    unsigned int bmap_nr = reiserfs_bmap_nr(blocks, fs->fs_blocksize);

    if (fs->fs_bitmap2)
	reiserfs_panic ("bitmap is initiaized already");

    fs->fs_bitmap2 = reiserfs_create_bitmap (blocks);
    if (!fs->fs_bitmap2)
	return -1;

    if (reiserfs_bmap_over(bmap_nr)) {
	     if (get_sb_bmap_nr(fs->fs_ondisk_sb) != 0) {
		reiserfs_warning (stderr, "%s: large file system has "
		                  "incorrect bitmap count %u. Should be 0 "
				  "to indicate overflow.\nPlease re-run with "
				  "--rebuild-sb to fix it.\n", __FUNCTION__,
				  get_sb_bmap_nr(fs->fs_ondisk_sb));
		return -1;
	     }
    } else if (bmap_nr != get_sb_bmap_nr (fs->fs_ondisk_sb)) {
	reiserfs_warning (stderr, "%s: wrong either bitmaps number,\n",
	                  __FUNCTION__);
	reiserfs_warning (stderr, "count of blocks or blocksize, run with "
	                  "--rebuild-sb to fix it\n");
	return -1;
    }

    return reiserfs_fetch_ondisk_bitmap (fs->fs_bitmap2, fs);
}

int reiserfs_create_ondisk_bitmap (reiserfs_filsys_t * fs)
{
    if (fs->fs_bitmap2)
	reiserfs_panic ("bitmap is initiaized already");

    fs->fs_bitmap2 = reiserfs_create_bitmap (get_sb_block_count (fs->fs_ondisk_sb));
    if (!fs->fs_bitmap2)
	return 0;

    return 1;
}

void reiserfs_close_ondisk_bitmap (reiserfs_filsys_t * fs)
{
    if (!fs->fs_bitmap2)
	return;
    reiserfs_flush_to_ondisk_bitmap (fs->fs_bitmap2, fs);
    reiserfs_free_ondisk_bitmap (fs);
}
