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

#include "debugreiserfs.h"


#if 0

/* this reads stdin and recover file of given key:  */
/* the input has to be in the follwong format:
   K dirid objectid
   N name
   B blocknumber
   ..
   then recover_file will read every block, look there specified file and put it into
*/
void do_recover (reiserfs_filsys_t fs)
{
    char name [100];
    char * line = 0;
    int n = 0;
    int fd;
    struct key key = {0, 0, };
    struct buffer_head * bh;
    struct item_head * ih;
    unsigned long block;
    char code;
    loff_t recovered = 0;
    int i, j;
    reiserfs_bitmap_t bitmap;
    int used, not_used;

    bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs));
    reiserfs_fetch_disk_bitmap (bitmap, fs);
    /* we check how many blocks recoverd items point to are free or used */
    used = 0;
    not_used = 0;

    fd = 0;
    while (getline (&line, &n, stdin) != -1) {
	if (line [0] == '#' || line [0] == '\n')
	    continue;
	switch (line [0]) {
	case 'K':
	    /* get a key of file which is to be recovered */
	    if (sscanf (line, "%c %u %u\n", &code, &key.k_dir_id, &key.k_objectid) != 3) {
		die ("recover_file: wrong input K format");
	    }
	    printf ("Recovering file (%u, %u)\n", key.k_dir_id, key.k_objectid);
	    break;

	case 'N':
	    /* get a file name */
	    recovered = 0;
	    if (sscanf (line, "%c %s\n", &code, name) != 2) {
		die ("recover_file: wrong input N format");
	    }
	    fd = open (name, O_RDWR | O_CREAT | O_EXCL, 0644);
	    if (fd == -1)
		die ("recover_file: could not create file %s: %s",
		     name,strerror (errno));
	    printf ("Recovering file %s..\n", name);
	    break;

	case 'B':
	    if (!fd)
		die ("recover_file: file name is not specified");
	    if (sscanf (line, "%c %lu\n", &code, &block) != 2) {
		die ("recover_file: wrong input B format");
	    }
	    bh = bread (fs->s_dev, block, fs->s_blocksize);
	    if (!bh) {
		printf ("reading block %lu failed\n", block);
		continue;
	    }

	    printf ("working with block %lu..\n", block);

	    ih = B_N_PITEM_HEAD (bh, 0);
	    for (i = 0; i < node_item_number (bh); i ++, ih ++) {
		__u32 * indirect;
		struct buffer_head * tmp_bh;

		if (!is_indirect_ih (ih) || key.k_dir_id != ih->ih_key.k_dir_id ||
		    key.k_objectid != ih->ih_key.k_objectid)
		    continue;

		indirect = (__u32 *)B_I_PITEM (bh, ih);
		for (j = 0; j < I_UNFM_NUM (ih); j ++) {
		    block = le32_to_cpu (indirect [j]);
		    if (!block)
			continue;
		    tmp_bh = bread (fs->s_dev, block, fs->s_blocksize);
		    if (!tmp_bh) {
			printf ("reading block %Lu failed\n", (loff_t)block * fs->s_blocksize);
			continue;
		    }
		    if (lseek64 (fd, get_offset (&ih->ih_key) + j * fs->s_blocksize - 1,
				 SEEK_SET) == (loff_t)-1) {
			printf ("llseek failed to pos %Ld\n", (loff_t)block * fs->s_blocksize);
			brelse (tmp_bh);
			continue;
		    }
		    if (reiserfs_bitmap_test_bit (bitmap, block))
			used ++;
		    else
			not_used ++;
		    /*printf ("block of file %Ld gets block %lu\n",
		      (get_offset (&ih->ih_key) - 1) / fs->s_blocksize + j, block);*/
		    if (write (fd, tmp_bh->b_data, tmp_bh->b_size) != tmp_bh->b_size) {
			printf ("write failed to pos %Ld\n", (loff_t)block * fs->s_blocksize);
			brelse (tmp_bh);
			continue;
		    }
		    recovered += fs->s_blocksize;
		    brelse (tmp_bh);
		}
	    }
	    brelse (bh);
	    break;
	}
    }
    printf ("recover_file: %Ld bytes recovered of file %s, key %u %u, %d blocks are free and %d are used\n",
	    recovered, name, key.k_dir_id, key.k_objectid, not_used, used);
}
#endif



/* read a file containing map of one or more files and either recover
   them or just print info */
/*
static void read_map (FILE * fp)
{
    int i;
    __u32 v32;
    char * buf;
    __u32 ids [4];
    int do_recover = 0;


    buf = 0;
    while (1) {
	if (fread (&v32, sizeof (v32), 1, fp) != 1)
	    break;
	if (v32 != MAP_MAGIC)
	    reiserfs_panic ("read_map: no magic found");

	// device name length and name itself 
	fread (&v32, sizeof (v32), 1, fp);
	buf = realloc (buf, v32);
	if (!buf)
	    reiserfs_panic ("realloc failed");
	fread (buf, v32, 1, fp);
	reiserfs_warning (stdout, "\"%s\": ", buf);

	// file name length and name itself
	fread (&v32, sizeof (v32), 1, fp);
	buf = realloc (buf, v32);
	if (!buf)
	    reiserfs_panic ("realloc failed");
	fread (buf, v32, 1, fp);

	// read directory key and poined object key 
	fread (ids, sizeof (ids), 1, fp);
	reiserfs_warning (stdout, "[%K]:\"%s\"-->[%K]\n",
			  &ids[0], buf, &ids[2]);

	
	//do_recover = user_confirmed (stdout, "recover? (Y):", "Y\n");
	//if (do_recover)
	//    reiserfs_warning (stderr, "recovering not ready\n");
	

	// how many data blocks are there 
	fread (&v32, sizeof (v32), 1, fp);
	if (v32) {
	    buf = realloc (buf, v32 * 4);
	    if (!buf)
		reiserfs_panic ("realloc failed (%u)", v32);
	    
	    // read list of data block numbers 
	    fread (buf, 4, v32, fp);
	    
	    if (!do_recover) {
		for (i = 0; i < v32; i ++)
		    reiserfs_warning (stdout, "%d ", ((__u32 *)buf)[i]);
		reiserfs_warning (stdout, "\n");
	    }
	}
	
	// main tail length 
	fread (&v32, sizeof (v32), 1, fp);
	if (v32) {
	    // there is tail 
	    buf = realloc (buf, v32);
	    if (!buf)
		reiserfs_panic ("realloc failed");
	    fread (buf, v32, 1, fp);
	    if (!do_recover)
		reiserfs_warning (stdout, "%d bytes long tail\n", v32);
	} else {
	    if (!do_recover)
		reiserfs_warning (stdout, "No tail\n");
	}

	if (fread (&v32, sizeof (v32), 1, fp) != 1)
	    break;
	if (v32 != MAP_END_MAGIC)
	    reiserfs_panic ("read_map: no magic found");
    }

    free (buf);
}


void do_recover (reiserfs_filsys_t * fs)
{
    FILE * fp;

    if (map_file (fs)) {
	fp = fopen (map_file (fs), "r");
	if (fp == 0) {
	    reiserfs_warning (stderr, "do_recover: fopen failed: %m");
	    return;
	}
    } else {
	reiserfs_warning (stderr, "Reading file map from stdin..\n");
	fflush (stderr);
	fp = stdin;
    }

    read_map (fp);

    if (fp != stdin)
	fclose (fp);
    
}
*/

#include <limits.h>

static long int get_answer(long int max) {
    char *answer, *tmp;    
    size_t n = 0;    
    long int result = 0;

    do {
	printf("Which should be left?: ");
	getline (&answer, &n, stdin);
	result = strtol (answer, &tmp, 0);
	if ((errno != ERANGE) && (result < max) && (result >= 0) &&
	    (answer != tmp))
	    break;
    } while (1);
    return result;
}

static void recover_items(FILE *fp, reiserfs_filsys_t * fs, FILE *target_file) {
    struct buffer_head *bh, *bh_pointed;
    struct item_head *ih;
    struct saved_item item, *cur;
    int size = sizeof(struct saved_item) - sizeof(struct saved_item *);
    struct saved_item *map = NULL;
    __u32 map_size = 0;
    int start = -1;
    unsigned int i, j;
    __u64 offset = 0, length;
    long int result = 0;
    unsigned long unfm_ptr;

    while (fread(&item, size, 1, fp) == 1) {
	map_size += sizeof(struct saved_item);
	map = realloc(map, map_size);
	memcpy((void *)map + map_size - sizeof(struct saved_item), &item, size);
    }
    
    for (i = 1, cur = map + 1; i <= map_size / sizeof(struct saved_item); i++, cur++) {
	bh = bread (fs->fs_dev, (cur - 1)->si_block, fs->fs_blocksize);
	if (!bh) {
	    reiserfs_warning (fp, "bread failed\n");
	    continue;
	}

	if (i == map_size / sizeof(struct saved_item)) {
	    if (start != -1) {
		reiserfs_print_item(stdout, bh, B_N_PITEM_HEAD (bh, (cur - 1)->si_item_num));
		result = get_answer(i - start) + start;
	    } else {
		result = i - 1;
	    }
	    
	    start = -1;
	} else if (is_direntry_ih(&(cur - 1)->si_ih) || is_stat_data_ih(&(cur - 1)->si_ih)) {
	    brelse(bh);
	    continue;
	} else { 
	    length = get_bytes_number(&(cur - 1)->si_ih, fs->fs_blocksize);
	    if (offset < get_offset(&(cur - 1)->si_ih.ih_key) + 
		get_bytes_number(&(cur - 1)->si_ih, fs->fs_blocksize))
		offset = get_offset(&(cur - 1)->si_ih.ih_key) + (length ? length - 1 : 0);
	
	    if (offset >= get_offset(&cur->si_ih.ih_key)) {
		/* Problem interval */
		if (start == -1)
		    start = i - 1;
	    
		printf("Problem item %d:\n", i - start - 1);
		reiserfs_print_item(stdout, bh, B_N_PITEM_HEAD (bh, (cur - 1)->si_item_num));
	    } else if (start != -1) {
		/* problem interval finished */
		printf("Problem item %d:\n", i - start - 1);
		reiserfs_print_item(stdout, bh, B_N_PITEM_HEAD (bh, (cur - 1)->si_item_num));
	    
		result = get_answer((long int)i - start) + start;
		start = -1;
	    } else {
		result = i - 1;
	    }
	}
	brelse(bh);
	
	if (start != -1)
	    continue;

	printf("write %ld\n", result);

	bh = bread (fs->fs_dev, (map + result)->si_block, fs->fs_blocksize);
	if (!bh) {
	    reiserfs_warning (fp, "bread failed\n");
	    continue;
	}

	fseek(target_file, get_offset(&(map + result)->si_ih.ih_key) - 1, SEEK_SET);
	ih = B_N_PITEM_HEAD (bh, (map + result)->si_item_num);
	if (is_direct_ih(ih)) {
	    fwrite(B_I_PITEM(bh, ih), (map + result)->si_ih.ih2_item_len, 1, target_file);
	} else if (is_indirect_ih(ih)) {
	    for (j = 0; j < I_UNFM_NUM (ih); j ++) {
		unfm_ptr = d32_get((__u32 *)B_I_PITEM(bh, ih), j);
		if (!unfm_ptr) {
		    fseek(target_file, fs->fs_blocksize, SEEK_CUR);
		    continue;
		}
		bh_pointed = bread (fs->fs_dev, unfm_ptr, fs->fs_blocksize);
		if (!bh_pointed) {
		    reiserfs_warning (fp, "bread failed\n");
		    continue;
		}
		fwrite(bh_pointed->b_data, fs->fs_blocksize, 1, target_file);
		brelse(bh_pointed);
	    }
	}
	brelse(bh);
    }

    free(map);
}

void do_recover(reiserfs_filsys_t * fs) {
    FILE *fp, *recovery;

    if (map_file (fs)) {
	fp = fopen (map_file (fs), "r");
	if (fp == 0) {
	    reiserfs_warning (stderr, "fopen failed: %m\n");
	    return;
	}
    } else {
	reiserfs_warning (stderr, "Reading file map from stdin..\n");
	fflush (stderr);
	fp = stdin;
    }

    if (!(recovery = fopen(recovery_file(fs), "w+"))) {
	reiserfs_warning (stderr, "fopen failed: %m\n");
	return;
    }

    
    recover_items(fp, fs, recovery);
    
    if (fp != stdin)
	fclose (fp);
    
    fclose(recovery);
}
