#include<stdio.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/ioctl.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<assert.h>
#include<sys/time.h>

#include<linux/ext2_fs.h>
#include<linux/fs.h>

#include<linux/linlog/dtfs_header.h>
#include<linux/linlog/confinfo.h>
#include<linux/linlog/linlog_conv.h>
#include<linux/linlog/ext2_structs.h>
#include<linux/linlog/linlog_ioctl.h>

#include"device.h"

float u_threshold = 1;
int debug_flag = 0;
int num_free_blocks_read = 0;

struct segment_node{
  int segment_num;
  struct live_block_node *blocks;
  struct segment_node *next;
};

struct live_block_node{
  inode_id inode_num;
  uint32 offset;
  uint32 log_block;
  struct live_block_node *next;
};



void read_atime();
void read_segusage(struct layout_descriptor*);
int determine_usage(unsigned char, int);
void get_live_blocks(struct layout_descriptor*, struct segment_node *);
void clean_segment(struct layout_descriptor*, struct segment_node *);
void print_as_binary(unsigned char);
void do_clean(struct layout_descriptor*, int, uint32);
void read_inode(int, struct ext2fs_inode *);
uint32 get_logblock_pointer_from_inode(struct layout_descriptor*,
                                    struct ext2fs_inode, uint32);
struct live_block_node * clean_partial_segment(struct layout_descriptor*, uint32,struct live_block_node *); 
void get_checkpoint_header(struct layout_descriptor*,
			   struct dtfs_checkpoint_header*, uint32);
void get_fsys_entry(struct layout_descriptor*, struct dtfs_fsys_entry*,
                                      uint32);
int append_live_block(uint32, uint32, uint32);
int can_clean_segment(struct layout_descriptor*, int);
int set_cleaning_flag(struct layout_descriptor*, int);
int can_clean_partial_segment(struct layout_descriptor*,int);


void append_live_blocks(struct live_block_node *head);
struct live_block_node *add_block_to_live_block_list(struct live_block_node*,
						   inode_id inum,
						  uint32 offset,
						  uint32 log_block);

struct segment_node *add_segment_to_cleaning_list(struct layout_descriptor*,
						  struct segment_node* ,
						  int segment_number);

float det_utilization(struct layout_descriptor*, int);
void utilization_partial_segment(struct layout_descriptor*, uint32, int *, int *);

int get_num_live_blocks(struct segment_node*);



int main(int argc, char* argv[]){
  

  struct layout_descriptor *l;
  char string[] = "/dev/hda6";
  global_cfg.device = string;

  if(argc > 4){
    fprintf(stderr, "usage: mrclean [-u utilization] [-d]\n");
    exit(1);
  }
  else if((argc == 3 && !strcmp(argv[1], "-u")) || (argc == 4 &&!strcmp(argv[1], "-u"))){
    
    u_threshold = atof(argv[2]);
    printf("threshhold = %f\n", u_threshold);
  }
  else if((argc == 4 && !strcmp(argv[3] ,"-d")) || (argc == 2 && !strcmp(argv[1], "-d"))){
    debug_flag = 1;
  }
  

  l = dev_init(0);
  
  if (NULL == l) {
    fprintf(stderr, "Cannot initialize device\n");
    return 2;
  }
  read_segusage(l);
  
dev_done(l);

  return 0;
}

/*reads the segusage file for the device, and cleans the segments which are in
  use*/
void read_segusage(struct layout_descriptor* l){

  int fd = open("/proc/dtfs/03:06/segusage", O_RDONLY);
  int i,j;
  struct segment_node *cleaning_list = NULL; /*list of segments which will
					       be cleaned*/
  struct segment_node *temp;  
  int num_live_blocks=0;
  int num_segments=0;
  long elapsed_time=0;

  unsigned char buf;
  FILE* cFile;
  struct timeval* tv1 = (struct timeval*) malloc(sizeof(struct timeval));
  struct timezone* tz1 = (struct timezone*) malloc(sizeof(struct timezone));
  struct timeval* tv2 = (struct timeval*) malloc(sizeof(struct timeval));
  struct timezone* tz2 = (struct timezone*) malloc(sizeof(struct timezone));
  struct timeval* tv3 = (struct timeval*) malloc(sizeof(struct timeval));
  struct timezone* tz3 = (struct timezone*) malloc(sizeof(struct timezone));
  struct timeval* tv4 = (struct timeval*) malloc(sizeof(struct timeval));
  struct timezone* tz4 = (struct timezone*) malloc(sizeof(struct timezone));

  sync();

  i=0;

  cleaning_list = NULL;

  //get time before we start running the cleaner
  gettimeofday(tv1,tz1);


  /*scan the segusage proc entry for this device, for each segment in 
    use, examine the segment furthur to determine if we want to clean it*/
  while (read(fd, &buf, sizeof(unsigned char)) > 0)
    {
      /*      if (buf > 0)
	      printf("i = %d %x\n", i, buf); */
      for(j = 0; j < sizeof(unsigned char)*8; j++){
	
	if(determine_usage(buf,j)){
	  if (debug_flag)
	    printf("segment %d in use\n", (i*8)+j+1);
	  
	  if(can_clean_segment(l, (i*8)+j+1)){
	    cleaning_list = add_segment_to_cleaning_list(l,cleaning_list,
							 (i*8)+j+1);
	    num_segments++;
	       if (debug_flag)
		 printf("segment %d can be cleaned\n", (i*8)+j+1);
	  }
	  else 
	    if (debug_flag)
	      printf("segment %d cannot be cleaned\n", (i*8)+j+1);
	}
      }
      i++;
    }
  close(fd);

  
  /*Clean the segments in the list*/
  gettimeofday(tv4,tz4);
  elapsed_time = (tv4->tv_sec-tv4->tv_sec)*1000000 + tv4->tv_usec-tv4->tv_usec;
  printf("time before refiling %d\n", elapsed_time);

  while(cleaning_list!=NULL){
    if (debug_flag)
      printf("cleaning segment %d\n",cleaning_list->segment_num);
    
    clean_segment(l, cleaning_list);
    
   
    sync();
    num_live_blocks += get_num_live_blocks(cleaning_list);

    temp = cleaning_list;
    cleaning_list = cleaning_list->next;
    free(temp);
  }
  sync();
  
  //get time before after running the cleaner
  gettimeofday(tv2,tz2);
  elapsed_time = (tv2->tv_sec-tv1->tv_sec)*1000000 + tv2->tv_usec-tv1->tv_usec;
  cFile = fopen("cleaner.log", "w");
  fprintf(cFile, "[\n");
  fprintf(cFile, "CandidateSegmentsRead = %d;\n", num_segments);
  fprintf(cFile, "TransferTimeBlock = %ld;\n", elapsed_time/(num_live_blocks ?  2*num_live_blocks:1));
  fprintf(cFile, "BlockSize = %d;\n", BLOCK_SIZE);
  fprintf(cFile, "LiveBlocks = %d;\n", num_live_blocks);
  fprintf(cFile, "TransferTimeSeg = %ld;\n", elapsed_time/
	  ((num_segments+num_live_blocks/l->seglen) ? (num_segments+num_live_blocks/l->seglen) :1));
  fprintf(cFile, "BlocksPerSeg = %d;\n", l->seglen);
  fprintf(cFile, "EmptyBlocks = %d;\n", num_free_blocks_read);
  fprintf(cFile, "]");
  printf("elapsed timeis for cleaning %ld\n", elapsed_time);
  gettimeofday(tv3, tz3);
  printf("%ld %ld \n", tv1->tv_sec, tv1->tv_usec);
  printf("%ld %ld \n", tv2->tv_sec, tv2->tv_usec);
  printf("%ld %ld \n", tv3->tv_sec, tv3->tv_usec);
  elapsed_time = (tv3->tv_sec-tv2->tv_sec)*1000000 + tv3->tv_usec-tv2->tv_usec;
  printf("secs is %ld usec is %ld", (tv3->tv_sec-tv2->tv_sec)*1000000, 
	 tv3->tv_usec-tv2->tv_usec);
  printf("elapsed timeis for writing %ld", elapsed_time);
}


struct live_block_node *
add_block_to_live_block_list(struct live_block_node* head,
			     inode_id inum,
			     uint32 offset,
			     uint32 log_block)
{
  
  struct live_block_node *temp;
  struct live_block_node *new = (struct live_block_node *) malloc(sizeof(struct live_block_node));
  new->inode_num = inum;
  new->offset = offset;
  new->log_block = log_block;
  new->next = NULL;

  temp = head;

  if(temp == NULL)
    head = new;
  else{
    
    while(temp->next!=NULL)
      temp=temp->next;
    temp->next = new;
  }

  return head;
  
}


/*Adds a segment node to the linked list of segments that need to be cleaned*/
struct segment_node *add_segment_to_cleaning_list(struct layout_descriptor* l,
						  struct segment_node* head, 
						  int segment_number){
  struct segment_node *temp;
  struct segment_node *new = (struct segment_node *) malloc(sizeof(struct segment_node));
  new->segment_num = segment_number;
  new->next = NULL;
  new->blocks = NULL;
  temp = head;

  if(temp == NULL)
    head = new;
  else{
    
    while(temp->next!=NULL)
      temp=temp->next;
    temp->next = new;
  }
  get_live_blocks(l,new);

  return head;
}

/*returns 1 if bit offset of char c is set, 0 otherwise*/
int determine_usage(unsigned char c, int offset){
  unsigned char mask = (unsigned char) 1;
  unsigned char returnvalue;
  if(offset < 0 || offset > 7)
    return -1;
  mask = mask << offset; 
  returnvalue = (c & mask);
  return (returnvalue >> offset);
}



void clean_segment(struct layout_descriptor* l, struct segment_node* seg){
  FILE *fs;
  char string[80];
    
  append_live_blocks(seg->blocks);
  sync();
  
  /*mark segment as clean */
  fs = fopen("/proc/dtfs/03:06/segusage", "w");
  if(fs == NULL)
    fprintf(stderr, "unable to open segusage file in proc, failed to mark segment %d as clean.\n", seg->segment_num);
  else {
    if(debug_flag)
      fprintf(stdout, "release_segment %d\n", seg->segment_num);
    /*fprintf(fs, "release_segment %d", seg->segment_num);*/
    sprintf(string,"echo \"release_segment %d\" > /proc/dtfs/03:06/segusage",
	    seg->segment_num);
    if(debug_flag)
    fprintf(stdout,"string: %s.\n",string); 
    system(string);
  }
  fclose(fs);
  
}


/*determine if the segment passed as segment can be cleaned, return 1 if
  yes, 0 otherwise*/ 
int can_clean_segment(struct layout_descriptor* l, int segment_number){
  
  uint32 segment_start;
  uint32 seg_len;
  int check_block;
 
  if(segment_number < 0){
    fprintf(stderr, "Invalid segment number in can_clean_segment\n");
    return -1;
  }
 
  /*determine location of checkpoint header for segment*/
  seg_len = dev_get_seglen(l, segment_number);
  segment_start=dev_get_segstart(l, segment_number);
  check_block = segment_start+(seg_len - 1);
 
  /*start with the first partial segment write*/
  return can_clean_partial_segment(l,check_block);
}

/*determines if we can clean one part of a segment corresponding to a 
  partial segment write*/
int can_clean_partial_segment(struct layout_descriptor* l,int check_block ){

  struct dtfs_checkpoint_header new_header;

  /*read the checkpoint header for the current partial segment*/
  get_checkpoint_header(l,&new_header, check_block);
  
    
  /*this is a live segment, we shouldn't clean it*/
  if(new_header.timestamp == 0)
    {
      if (debug_flag)
	printf("segment is alive!!\n");
      return 0;
    }
 

  /*This header has a flag set which would prevent it from being cleaned*/
  if (new_header.segment_flags & (SEG_LOCKED|SEG_HAS_BADBLOCK
				  |SEG_CLEAN|SEG_IN_CLEANING))
    {
      if (new_header.segment_flags & SEG_LOCKED)
	printf("SEG LOCKED");
      if (new_header.segment_flags & SEG_HAS_BADBLOCK)
	printf("SEG HAS BAD BLOCK");
      if (new_header.segment_flags & SEG_CLEAN )
	printf("SEG CLEAN");
      if (new_header.segment_flags & SEG_IN_CLEANING)
	printf("SEG IN CLEANING");

      fprintf(stdout,"segment has a flag set wanker\n");
      return 0;
    }
  
  /*base case of recursion: is this the last partial segment write in this
    segment?*/
  else if(new_header.off_next_local==0)
    return 1;

  return can_clean_partial_segment(l,check_block - new_header.off_next_local);
}

 
struct live_block_node * clean_partial_segment(struct layout_descriptor* l, 
					       uint32 checkpoint_blocknum, 
				     struct live_block_node *live_block_list){

  int bytes_read,i;
  char bytearray[DTFS_MAX_LOGBLKSIZE];
  uint32 pointer;
  struct ext2fs_inode inode;
  struct dtfs_checkpoint_header new_header;
  struct dtfs_fsys_entry *curr_fsys_entry;
  struct dtfs_blkdesc_entry *curr_blk_entry;

  get_checkpoint_header(l,&new_header, checkpoint_blocknum);
  
  bytes_read =
    dev_read_block(l, checkpoint_blocknum,
                   bytearray, global_cfg.log_blksize);
  curr_fsys_entry = (struct dtfs_fsys_entry *)
    (bytearray+sizeof(struct dtfs_checkpoint_header));

  ntoh_fsys_entry(curr_fsys_entry, curr_fsys_entry);
  
  if (debug_flag){
    printf("============================\n");
    printf("reading new partial segment\n");
    
    printf("size of segment = %d\n", new_header.segsize);
    printf("previous header at %lu\n", (unsigned long) new_header.prev_segment);
    printf("next header at %lu\n", (unsigned long) new_header.next_segment);
    printf("next local chkpoint at %u\n", new_header.off_next_local);
    printf("checkpoint type = %d\n",(int) new_header.type);
    printf("locked = %o\n", new_header.segment_flags & SEG_LOCKED);
    printf("clean = %o\n", new_header.segment_flags & SEG_CLEAN);
    printf("badblocks = %o\n", new_header.segment_flags & SEG_HAS_BADBLOCK);
    printf("flags = %d\n", new_header.segment_flags);

    printf("num fs blocks = %d\n", curr_fsys_entry->num_blocks);
    printf("inode length = %d\n",  curr_fsys_entry->len_inode);
  }

  for(i = 0; i < curr_fsys_entry->num_blocks; i++){
    curr_blk_entry =
      (struct dtfs_blkdesc_entry *)
      (bytearray+sizeof(struct dtfs_checkpoint_header) +
       sizeof(struct dtfs_fsys_entry) + curr_fsys_entry->len_inode+(i*sizeof(struct dtfs_blkdesc_entry)));
    
    ntoh_blkdesc_entry(curr_blk_entry, curr_blk_entry);
    
    if (debug_flag){
      printf("\n\tblock %d: type:%c time:%d inode#%u offset:%u\n",
	     i, curr_blk_entry->optype,curr_blk_entry->mtime,
	     curr_blk_entry->owner, curr_blk_entry->offset);
      printf("\tthis block is located in block %d\n",
	     checkpoint_blocknum-(i+1));
    }
    /* if(curr_blk_entry->owner!=DTFS_IFILE_INODE) */
    read_inode((int) curr_blk_entry->owner, &inode);
    /*    else
      {
        memcpy(&inode, (void *) (bytearray+
                                 sizeof(struct dtfs_checkpoint_header) +
                                 sizeof(struct dtfs_fsys_entry)),
               curr_fsys_entry->len_inode);
      }
    */
    pointer = get_logblock_pointer_from_inode(l, inode, curr_blk_entry->offset);
    
    if(pointer >= 0){
      if (debug_flag)
	printf("\tInode points to this block: %u\n",pointer);
      
      if(pointer==checkpoint_blocknum-(i+1)&&inode.i_links_count>0){

	live_block_list = add_block_to_live_block_list(live_block_list,
							 curr_blk_entry->owner,
							curr_blk_entry->offset,
							 pointer);
      }
      else {
	printf("Free block found:  block # %d", i);
	num_free_blocks_read++;
      }
    }
    else {
      if(debug_flag)
	printf("offset too big\n");
      printf("Free block found:  block # %d", i);
      num_free_blocks_read++;
    }
  }  


 
  if(new_header.off_next_local > 0)
    live_block_list = clean_partial_segment(l,checkpoint_blocknum-new_header.off_next_local, live_block_list);

  return live_block_list;
  
}

void append_live_blocks(struct live_block_node *head){
  struct live_block_node * temp;

  temp = head;
  while(temp!=NULL){
    if(debug_flag)
      printf("inode number that we're cleaning: %d, offset: %d log block: %d\n", temp->inode_num, temp->offset, temp->log_block);
    temp=temp->next;
  }
  fflush(stdout);
  while(head!=NULL){
    if(debug_flag)
      printf("about to append %d, offset: %d log block: %d\n", 
	     head->inode_num, head->offset, head->log_block);
    fflush(stdout);
    
    
    if(append_live_block(head->inode_num,head->offset, head->log_block)< 0){
      fprintf(stderr, "error appending phys block %d to log\n", 
	      head->log_block);
      exit(1);
      }
    
    

    /* sync(); */ /*slows things down considerably- 1 blk per partial seg. */
    temp = head;
    head = head->next;
    free(temp);
  }

}

/*This function retrieves a checkpoint header from the logical block number
  passed in "location".*/
void get_checkpoint_header(struct layout_descriptor* l, 
			   struct dtfs_checkpoint_header* new_header, 
			   uint32 location)
{
  int bytes_read;
  bytes_read = dev_read_block(l, location, (char *) new_header,
                              sizeof(struct dtfs_checkpoint_header));
  ntoh_checkpoint_header(new_header, new_header);

  if(bytes_read != sizeof(struct dtfs_checkpoint_header))
    fprintf(stderr, "unable to read checkpoint header at block %u\n",location);
}

int append_live_block(uint32 inum, uint32 logblock, uint32 physblock){
  
  struct dtfs_cleaner_info cli;
  int fd;
  int result=0;
  int j;
  printf("from cleaner.c inum = %u", inum);
  printf("Appending logblock %d of inode %d\n", logblock, inum);
  printf("Appending %d", physblock);
  fflush(stdout);

  fd = open("/linlog/.ifile", O_RDONLY);
  if(fd < 0)
    return -1;
  cli.inum=inum;
  cli.log_blknum = logblock;
  cli.phys_blknum = physblock;
  //  scanf("%d", &j);
  result = ioctl(fd, DTFS_IOC_REFILE_BLOCK, &cli);
  printf("result = %d", result);
  //  scanf("%d", &j);
  close(fd);
  return result;
}

void get_fsys_entry(struct layout_descriptor* l, struct dtfs_fsys_entry* entry,
		    uint32 location)
{
  int bytes_read;
  char bytearray[DTFS_MAX_LOGBLKSIZE];
  bytes_read =
    dev_read_block(l, location, bytearray, global_cfg.log_blksize);
  if(bytes_read != global_cfg.log_blksize)
    fprintf(stderr, "unable to read fsys entry from block %u\n", location);

  memcpy(entry, bytearray+sizeof(struct dtfs_checkpoint_header), 
	 sizeof(struct dtfs_fsys_entry));
  ntoh_fsys_entry(entry, entry);
}


/*Given an inode and the block offset in the file corresponding to the inode,
  this function returns the logical block in the filesystem to which this
  inode currently points for the given block offset*/

uint32 get_logblock_pointer_from_inode(struct layout_descriptor* l, 
				    struct ext2fs_inode inode, uint32 offset)
{
    

  int bytes_read;
  /*int addr_per_block = EXT2_ADDR_PER_BLOCK(inode.i_sb);*/
  uint32 addr_per_block = global_cfg.log_blksize/sizeof(uint32);
  uint32 ind_addresses[DTFS_MAX_LOGBLKSIZE];
  
  if (debug_flag)
    printf("\tinode links count %d\n", inode.i_links_count);
  if(offset < EXT2_NDIR_BLOCKS)  /*If the block ptr is stored in inode*/
    return inode.i_block[offset];
  

  else if(offset < addr_per_block+EXT2_NDIR_BLOCKS)
    { /*If block pointer is in single indirect block*/
      if (debug_flag)
	printf("Address of indirect block: %u\n",inode.i_block[EXT2_IND_BLOCK]);
	
      bytes_read = dev_read_block(l, inode.i_block[EXT2_IND_BLOCK], 
			 (char *)ind_addresses, global_cfg.log_blksize);
	
      if(bytes_read != global_cfg.log_blksize)
	  fprintf(stderr, "unable to read indirect block from device.\n");
      
      return ind_addresses[offset-EXT2_NDIR_BLOCKS];
    }
  else if(offset == IND_1_START){
    return inode.i_block[EXT2_IND_BLOCK];
  }
  else if(offset > IND_1_START && offset < IND_2_START){
    uint32 indirect_block_offset = offset - IND_1_START -1;
    if (debug_flag)
      printf("indirect block offset = %u\n", indirect_block_offset);
    
    if(indirect_block_offset > addr_per_block)
      fprintf(stdout, "error: block offset too large for double indirect block\n");
    if (debug_flag)
      printf("1st double indirect block at %u\n",inode.i_block[EXT2_DIND_BLOCK]);
    
    bytes_read = dev_read_block(l, inode.i_block[EXT2_DIND_BLOCK],
				(char *)ind_addresses, global_cfg.log_blksize);
    return ind_addresses[indirect_block_offset];
  }
  /*BUGBUGBUGBUG - need similar case to the one immediately above in order
    to deal with triple indirect blocks*/
  else if(offset == IND_2_START){
    return inode.i_block[EXT2_DIND_BLOCK];
  }
  else if(offset == IND_3_START){
    return inode.i_block[EXT2_TIND_BLOCK];
  }
  else if (offset > IND_1_START){
    
    printf("error: offset too large: %d\n", offset);
    
    return -1;
  }
    
  else if(offset < (addr_per_block * addr_per_block)+
	  addr_per_block+EXT2_NDIR_BLOCKS) 
    {
    /*code to deal with double indirect block*/
    
    uint32 indirect_block_offset = 
      (offset - EXT2_NDIR_BLOCKS - addr_per_block)/addr_per_block;
   
    uint32 dbl_indirect_block_offset =
      (offset - EXT2_NDIR_BLOCKS - addr_per_block)%addr_per_block; 

    if(debug_flag){
      printf("\tindirect block offset = %u\n",indirect_block_offset);
	  printf("\tdouble indirect block offset = %u\n",dbl_indirect_block_offset);
    }
    bytes_read = dev_read_block(l, inode.i_block[EXT2_DIND_BLOCK],
				(char *)ind_addresses, global_cfg.log_blksize);
    if (debug_flag)
      printf("\treading single indirect block at %u\n",
	     inode.i_block[EXT2_DIND_BLOCK]);
    if(bytes_read != global_cfg.log_blksize)
      fprintf(stdout, "unable to read indirect block from device.\n");
    
    if (debug_flag)
      fprintf(stdout, "\tdouble indirect block at %u\n", 
	      ind_addresses[indirect_block_offset]);
    /*for(i = 0; i < 8; i++)
      fprintf(stdout, "indir address %d: %u\n", i, ind_addresses[i]);*/
    bytes_read = dev_read_block(l, ind_addresses[indirect_block_offset],
				(char *)ind_addresses, global_cfg.log_blksize);
    if(bytes_read != global_cfg.log_blksize){
      fprintf(stdout, "unable to read 2nd indirect block from device.\n");
          
    }
    return ind_addresses[dbl_indirect_block_offset];
  }
  else if(offset < (addr_per_block*addr_per_block*addr_per_block)+
	  (addr_per_block * addr_per_block)+
          addr_per_block+EXT2_NDIR_BLOCKS){
    /*code to deal with triple indirect block*/
   
    uint32 indirect_block_offset = (offset-EXT2_NDIR_BLOCKS-addr_per_block - 
				    (addr_per_block*addr_per_block))/
                                    addr_per_block*addr_per_block;
    uint32 dbl_indirect_block_offset=(offset-EXT2_NDIR_BLOCKS-addr_per_block -
                                    (addr_per_block*addr_per_block))
                                    /addr_per_block;
    uint32 triple_indirect_block_offset=(offset-EXT2_NDIR_BLOCKS-
					 addr_per_block -
					 (addr_per_block*addr_per_block))
                                         %addr_per_block;

    bytes_read = dev_read_block(l, inode.i_block[EXT2_TIND_BLOCK],
                                (char *)ind_addresses, global_cfg.log_blksize);
    if(bytes_read != global_cfg.log_blksize)
      fprintf(stdout, "unable to read indirect block from device.\n");
    
    bytes_read = dev_read_block(l, ind_addresses[indirect_block_offset],
                                (char *)ind_addresses, global_cfg.log_blksize);
    if(bytes_read != global_cfg.log_blksize)
      fprintf(stdout, "unable to read 2nd indirect block from device.\n");

    bytes_read = dev_read_block(l, ind_addresses[dbl_indirect_block_offset],
                                (char *)ind_addresses, global_cfg.log_blksize);
    if(bytes_read != global_cfg.log_blksize)
      fprintf(stdout, "unable to read 2nd indirect block from device.\n");

    return ind_addresses[triple_indirect_block_offset];
  }

  return -1; /*offset is too large*/
}

void read_inode(int inum, struct ext2fs_inode *data)
{
  int fd, bytesread;
  fd = open("/linlog/.ifile", O_RDONLY);
  
  if(fd < 0){
    fprintf(stderr,"unable to open /linlog/.ifile\n");
    perror("");
  }
  lseek(fd, inum*sizeof(struct ext2fs_inode), SEEK_SET);
  bytesread=read(fd, data, sizeof(struct ext2fs_inode));
  
  if(bytesread!=sizeof(struct ext2fs_inode))
    fprintf(stderr,"unable to read inode %d\n", inum);
  
  close(fd);
}
  
void print_as_binary(unsigned char c){
  
  unsigned char tmp;
  unsigned char mask = (unsigned char) 1;
  int i;
  for (i = 0; i < sizeof(unsigned char)*8; i++){
    tmp = c & mask;
    
    if (debug_flag)
      printf("%d", tmp >> i);
    mask = mask << 1;
  }
}


/* determine the utilization of this segment */
float det_utilization(struct layout_descriptor* l, int segment_number){
  uint32 segment_start;
  uint32 seg_len;
  
 
  int num_blocks = 0;
  int num_live_blocks = 0;

  seg_len = dev_get_seglen(l, segment_number);
  segment_start=dev_get_segstart(l, segment_number);


  /* find the utilization recursively */
  
  
  utilization_partial_segment(l,segment_start+(seg_len - 1),
			      &num_blocks, &num_live_blocks);
  
  if (debug_flag)
    printf("utilization of segment %d: %d live %d total  %f%%\n", 
	   segment_number,
	   num_live_blocks,
	   num_blocks,
	   (float) num_live_blocks / num_blocks); 
  return ((float) num_live_blocks / num_blocks);
    
}








void utilization_partial_segment(struct layout_descriptor* l, 
			   uint32 checkpoint_blocknum, 
			   int *num_blocks, int *num_live_blocks){

  int bytes_read,i;
  char bytearray[DTFS_MAX_LOGBLKSIZE];
  uint32 pointer;
  struct ext2fs_inode inode;
  struct dtfs_checkpoint_header new_header;
  struct dtfs_fsys_entry *curr_fsys_entry;
  struct dtfs_blkdesc_entry *curr_blk_entry;

  get_checkpoint_header(l,&new_header, checkpoint_blocknum);
  
  bytes_read =
    dev_read_block(l, checkpoint_blocknum,
                   bytearray, global_cfg.log_blksize);
  curr_fsys_entry = (struct dtfs_fsys_entry *)
    (bytearray+sizeof(struct dtfs_checkpoint_header));

  ntoh_fsys_entry(curr_fsys_entry, curr_fsys_entry);


  *num_blocks = curr_fsys_entry->num_blocks;

  for(i = 0; i < curr_fsys_entry->num_blocks; i++){
    
    curr_blk_entry =
      (struct dtfs_blkdesc_entry *)
      (bytearray+sizeof(struct dtfs_checkpoint_header) +
       sizeof(struct dtfs_fsys_entry) + curr_fsys_entry->len_inode+(i*sizeof(struct dtfs_blkdesc_entry)));
    
    ntoh_blkdesc_entry(curr_blk_entry, curr_blk_entry);
    
  
 
    if(curr_blk_entry->owner!=DTFS_IFILE_INODE)
      read_inode((int) curr_blk_entry->owner, &inode);
    else
      {
        memcpy(&inode, (void *) (bytearray+
                                 sizeof(struct dtfs_checkpoint_header) +
                                 sizeof(struct dtfs_fsys_entry)),
               curr_fsys_entry->len_inode);
      }
    pointer = get_logblock_pointer_from_inode(l, inode, curr_blk_entry->offset);
    
    if(pointer >= 0){
 
      if(pointer==checkpoint_blocknum-(i+1)&&inode.i_links_count>0){
	*num_live_blocks = *num_live_blocks + 1;
      }
    }
    else
      printf("offset too big\n");
  }  


 
  if(new_header.off_next_local > 0)
    utilization_partial_segment(l,checkpoint_blocknum-new_header.off_next_local,
			       num_blocks, num_live_blocks);

  return;
  
}



void get_live_blocks(struct layout_descriptor* l, struct segment_node *seg){
  
  uint32 segment_start;
  uint32 seg_len;
  
  seg_len = dev_get_seglen(l, seg->segment_num);
  segment_start = dev_get_segstart(l, seg->segment_num);
  
  if (debug_flag){
    printf("\nBEGIN SEGMENT %d\n\n", seg->segment_num);
    printf("segment %d starts at logical blk %u\n", seg->segment_num,
         segment_start);
  }

  /* do the appending of live blocks */
  printf("segment number is %d", seg->segment_num);
  seg->blocks = clean_partial_segment(l,segment_start+(seg_len - 1), 
					  seg->blocks);
}


int get_num_live_blocks(struct segment_node* s) {
  int cnt = 0;
  struct live_block_node* lb = s->blocks;

  while (lb != NULL) {
    lb = lb->next;
    cnt++;
  }
  return cnt;
}
