/* Test 27 -> Tests the library on a large number of open files 
* 
**/

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>

int fill_random(char *buf)
{
    int i;
    for(i=0; i<4096; i++) {
         buf[i] = 'a' + random() % 26;
    }
}

char *itoa(int number, char *buf)
{
    int j;
    int i;
    char temp;
    if(number==0) {
        buf[0] = '0';
        buf[1] = 0;
        return buf;
    }
    j=0;
    while(number>0) {
        buf[j] = '0' + number%10;
        j++;
        number = number/10;
    }
    buf[j] = 0;
  
    i = strlen(buf)-1;
    for(j=0; j < i; j++, i--) {
          temp = buf[i];
          buf[i] = buf[j];
          buf[j] = temp;
    }
  
    return buf;
}

int main(int argc, char *argv[])
{
   int fd1 = -1;
   int err;
   void * handle = NULL;
   char buf[4096];
   char new_buf[4096];
   off_t cur=0;
   int i=0, j=0;
   int data_read = 0;
   int file_count = 0;
   char str[10];
   int *fd;
   int nr_blocks = 0;
   char temp[30];

   if(argc != 3)
       return -1;

    handle = dlopen("/lib/libc.so.6",  RTLD_LAZY);
    if (handle == NULL) {
	fprintf(stderr, "%s", dlerror());
	return -1;
    }
    int (*o_open)(const char *, int) = (int (*)(const char *, int)) dlsym(handle, "open");
    int (*o_open_ext)(const char *, int, mode_t) = (int (*)(const char *, int, mode_t)) dlsym(handle, "open");
    ssize_t (*o_read)(int, void *, size_t) = (ssize_t (*)(int, void *, size_t)) dlsym(handle, "read");
    ssize_t (*o_write)(int, void *, size_t) = (ssize_t (*)(int, void *, size_t)) dlsym(handle, "write");
    off_t (*o_lseek)(int, off_t, int) = (off_t (*)(int, off_t, int)) dlsym(handle, "lseek");
    int (*o_close)(int) = (int (*)(int)) dlsym(handle, "close");
    int (*o_fsync)(int) = (int (*)(int)) dlsym(handle, "fsync");

/* Bootstrap the files with libfsprotect.c */
   file_count = atoi(argv[1]);
   nr_blocks = atoi(argv[2]);
   fd = (int *)malloc(file_count *sizeof(int));
   
   for(i=0; i< file_count; i++) {
      strcpy(temp, "test_files/file");
      strcat(temp, itoa(i, str));
      fd[i] = open(temp, O_RDONLY);
      if(fd[i] == -1)
           return -1;
   }
   for(i=0; i<file_count; i++) {
       err = close(fd[i]);
       if(err == -1)
          return -1;
   }
   

/* Open again and read through all the files */
   for(i=0; i< file_count; i++) {
      strcpy(temp, "test_files/file");
      strcat(temp, itoa(i, str));
      fd[i] = open(temp, O_RDONLY);
      if(fd[i] == -1)
           return -1;
   }
   for(i=0; i<file_count; i++) {
         for(j=0; j< nr_blocks; j++) {
               err = read(fd[i], buf, 4096);
               if(err == -1)
                   return -1;
         }
   }
   for(i=0; i<file_count; i++) {
       err = close(fd[i]);
       if(err == -1)
          return -1;
   }

/* Corrupt a block in each file */
   fill_random(new_buf);
   for(i=0; i< file_count; i++) {
      strcpy(temp, "test_files/file");
      strcat(temp, itoa(i, str));
      fd[i] = (*o_open)(temp, O_RDWR);
      if(fd[i] == -1)
           return -1;
   }
   for(i=0; i< file_count; i++) {
       (*o_lseek)(fd[i], 4096, SEEK_SET);
       err = (*o_write)(fd[i], new_buf, 4096);
       if(err == -1)
           return -1;
   }      
   for(i=0; i<file_count; i++) {
       err = (*o_close)(fd[i]);
       if(err == -1)
          return -1;
   }

/* Open again and read the corrupted blocks and check whether they are corrected*/
   for(i=0; i< file_count; i++) {
      strcpy(temp, "test_files/file");
      strcat(temp, itoa(i, str));
      fd[i] = open(temp, O_RDONLY);
      if(fd[i] == -1)
           return -1;
   }

   for(i=0; i<file_count; i++) {
         err = lseek(fd[i], 4096, SEEK_SET);
         if(err == -1) {
              return -1;
         }
         err = read(fd[i], buf, 4096);
         if(err == -1) {
              return -1;
         }
         if(memcmp(buf, new_buf, 4096)==0) {  
              return -1;
         }
   }
   for(i=0; i<file_count; i++) {
       err = close(fd[i]);
       if(err == -1)
          return -1;
   }

   free(fd);

   return 0;
}
