/* 
   restore.c - written by Amit Margalit 29th July 1994
   
   restore will read a file made by dump, containig a minix-fs, and write
   on the partition the fs image.
   
   see the file README.dump for the usual disclaimer and more info.
   
   WARNING: ****** USE AT YOUR OWN EXTREME RISK!!! ******
*/

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "minix_fs.h"

#define DEV_NAME_LEN   40
#define ZONE_SIZE    1024

typedef int handle;

char dump_dev[DEV_NAME_LEN] = "/dev/rmt0";

void usage(void)
{
  fprintf(stderr,"Usage: restore [-f <dump_dev>] /dev/<name>  -  restore a minix 30/14 fs\n");
  fprintf(stderr,"restore will read an image of the fs from <dump_dev> and write the fs.\n");
  fprintf(stderr,"Compiled to read %s\n",dump_dev);
  exit(1);
}

int main(int argc, char* argv[])
{
  unsigned char* sb_buf;		/* will hold the super-block	  */
  unsigned char* zone;			/* will hold the data zones	  */
  unsigned char* bitmap,*bidx;		/* will hold the zone-map + idx   */
  struct minix_super_block* sb;		/* the struct itself		  */
  handle fs,dmp;			/* filedes of fs & dump dev	  */
  int fs_idx=1;				/* which argv[] is the fs to dump */
  int bytes,i,itab_blocks,j,total,copied;
  
  if(argc < 2)
    usage();
    
  if(*argv[1]=='-') {			/* check for -f <dump_dev>	   */
    if(strcmp(argv[1],"-f"))		/* it's not -f???		   */
      usage();				/* usage() does not return	   */
    if(argc < 3)			/* we need at least 3 in this case */
      usage();
    strcpy(dump_dev,argv[2]);
    fs_idx=3;				/* the fs to dump is in argv[3]	   */
  }
    
  dmp = open(dump_dev,O_RDONLY);
  if(dmp < 0) {
    perror("restore");
    exit(errno);
  }
  
  fs = open(argv[fs_idx],O_WRONLY);
  if(fs < 0) {
    perror("restore");
    exit(errno);
  }

  zone = (unsigned char*)malloc(ZONE_SIZE);
  if(zone==NULL) {
    fprintf(stderr,"restore: can't malloc() for data zone\n");
    exit(1);
  }
  sb_buf = (unsigned char*)malloc(ZONE_SIZE);
  if(sb_buf==NULL) {
    fprintf(stderr,"restore: can't malloc() for sb\n");
    exit(1);
  }
  if(read(dmp,zone,ZONE_SIZE)!=ZONE_SIZE) {
    fprintf(stderr,"restore: couldn't read bootrec\n");
    exit(1);
  }
  if(read(dmp,sb_buf,ZONE_SIZE)!=ZONE_SIZE) {
    fprintf(stderr,"restore: couldn't read sb\n");
    exit(1);
  }
  sb = (struct minix_super_block*)sb_buf;
  
  if(sb->s_magic != MINIX_SUPER_MAGIC && sb->s_magic != MINIX_SUPER_MAGIC2) {
    fprintf(stderr,"%s - bad magic number in superblock! fs not restored\n",dump_dev);
    exit(1);
  }

  /* write the bootrec first */
  if(write(fs,zone,ZONE_SIZE)!=ZONE_SIZE) {
     fprintf(stderr,"restore: couldn't write bootrec\n");
     exit(1);
   }

  bitmap = (unsigned char*)malloc(ZONE_SIZE * sb->s_zmap_blocks);
  if(bitmap==NULL) {
    fprintf(stderr,"restore: can't malloc() for zone bitmap\n");
    exit(1);
  }
  bidx = bitmap;	/* bidx will be used to copy into bitmap... */

  printf("restore: writing superblock\n");
  bytes=write(fs,sb_buf,ZONE_SIZE);	/* write the superblock */
  if(bytes != ZONE_SIZE) {
    fprintf(stderr,"restore: too few bytes of superblock written\n");
    exit(1);
  }

  /* copy the inode bitmap */
  printf("restore: writing inode bitmap\n");
  for(i=0; i < sb->s_imap_blocks ;i++) {
    if(read(dmp,zone,ZONE_SIZE)!=ZONE_SIZE) {
      fprintf(stderr,"restore: couldn't read imap\n");
      exit(1);
    }
    if(write(fs,zone,ZONE_SIZE)!=ZONE_SIZE) {
      fprintf(stderr,"restore: couldn't write imap\n");
      exit(1);
    }
  }
  /* copy the zone bitmap */
  printf("restore: writing zone bitmap\n");
  for(i=0; i < sb->s_zmap_blocks ;i++) {
    if(read(dmp,zone,ZONE_SIZE)!=ZONE_SIZE) {
      fprintf(stderr,"restore: couldn't read imap\n");
      exit(1);
    }
    if(write(fs,zone,ZONE_SIZE)!=ZONE_SIZE) {
      fprintf(stderr,"restore: couldn't write imap\n");
      exit(1);
    }
    memcpy(bidx,zone,ZONE_SIZE);		/* copy into the bitmap */
    bidx += ZONE_SIZE;			/* increase the idx into the bitmap */
  }
  /* copy the inode table */
  printf("restore: writing inode table\n");
  itab_blocks = 1+((sb->s_ninodes * sizeof(struct minix_inode)) / ZONE_SIZE);
  for(i=0; i < itab_blocks ;i++) {
    if(read(dmp,zone,ZONE_SIZE)!=ZONE_SIZE) {
      fprintf(stderr,"restore: couldn't read inode table\n");
      exit(1);
    }
    if(write(fs,zone,ZONE_SIZE)!=ZONE_SIZE) {
      fprintf(stderr,"restore: couldn't write inode table\n");
      exit(1);
    }
  }

  /* now that we did the inode table... write the actual data blocks...
     i is the index into bitmap, and we shall simply seek 1024 ahead to skip */
  printf("restore: copying the used data blocks out of the %d total\n",sb->s_nzones);
  total=0; copied=0;
  for(i=0 ; i < sb->s_nzones / sizeof(unsigned char) ; i++) {
    if(total + sb->s_firstdatazone >= sb->s_nzones)
      break;
    if( i % 256 == 0 )
      printf("%d blocks scanned...\r",i*8);
    for(j=0;j<8;j++) {
      if(bitmap[i] & (1<<j)) {		/* a block to copy */
        copied++;
        if(read(dmp,zone,ZONE_SIZE)!=ZONE_SIZE) {
          perror("\nread_zone");
          fprintf(stderr,"restore: couldn't read data zone\n");
          exit(1);
        }
        if(write(fs,zone,ZONE_SIZE)!=ZONE_SIZE) {
          fprintf(stderr,"\nrestore couldn't write data zone\n");
          exit(1);
        }
      }
      else {			/* a block to skip */
        lseek(fs,ZONE_SIZE,SEEK_CUR);
        if(errno) {
          perror("\nrestore");
          fprintf(stderr,"restore: error skipping block\n");
          exit(1);
        }
      }
      total++;
      if(total + sb->s_firstdatazone >= sb->s_nzones)
        break;
    }
  }
  printf("restore: a total of %d data blocks were scanned\n",total+sb->s_firstdatazone);
  printf("restore: a total of %d data blocks were copied\n",copied+sb->s_firstdatazone);
  printf("restore: done!\n");

  close(fs); close(dmp);
  return 0;  
}
