// u7-mini-loesung.c

// Hans-Georg Esser, Systemprogrammierung, 2012/06/08

// Musterloesung
// - ohne Fehlerkontrolle
// - wertet Superblock nicht fuer Zugriff auf Inode- und
//   Zone-Listen aus, funktioniert also nur mit Beispiel-Images
// - ohne Optimierungen, viele unnuetze Lese-/Schreibzugriffe
// - kann zur Not als Basis fuer naechste Aufgaben dienen

#include <sys/fcntl.h>  // O_RDONLY etc.
#include <stdint.h>     // uint16_t etc.
#include <stdio.h>      // printf

// Datei fest vorgegeben
#define IMAGEFILENAME "minix1.img"
#define BLOCK_SIZE 1024

struct minix_superblock { 
  uint16_t s_ninodes;        uint16_t s_nzones; 
  uint16_t s_imap_blocks;    uint16_t s_zmap_blocks; 
  uint16_t s_firstdatazone;  uint16_t s_log_zone_size; 
  uint32_t s_max_size;       uint16_t s_magic; 
  uint16_t s_state;          uint32_t s_zones;
};

void readblock (int blockno, unsigned char* block) {
  // Aufgabe a)
  int fd = open (IMAGEFILENAME, O_RDONLY);  // read only oeffnen
  lseek (fd, blockno * BLOCK_SIZE);         // Sprung zum richtigen Block
  read (fd, block, BLOCK_SIZE);             // Block lesen
  close (fd);                               // wieder schliessen
  return;
};

void writeblock (int blockno, unsigned char* block) {
  // Aufgabe a)
  int fd = open (IMAGEFILENAME, O_WRONLY);  // write only oeffnen
  lseek (fd, blockno * BLOCK_SIZE);         // Sprung zum richtigen Block
  write (fd, block, BLOCK_SIZE);            // Block schreiben
  close (fd);                               // wieder schliessen
  return;
};

void show_superblock() {
  // Aufgabe b)
  unsigned char block[1024];
  struct minix_superblock* sblock;
  readblock (1, (unsigned char*)&block);    // Superblock = Block 1
  sblock = (struct minix_superblock*) &block;
  printf ("s_ninodes:       %d\n", sblock->s_ninodes);
  printf ("s_nzones:        %d\n", sblock->s_nzones); 
  printf ("s_imap_blocks:   %d\n", sblock->s_imap_blocks); 
  printf ("s_zmap_blocks:   %d\n", sblock->s_zmap_blocks); 
  printf ("s_firstdatazone: %d\n", sblock->s_firstdatazone); 
  printf ("s_log_zone_size: %d\n", sblock->s_log_zone_size); 
  printf ("s_max_size:      %d\n", sblock->s_max_size); 
  printf ("s_magic:         %d\n", sblock->s_magic); 
  printf ("s_state:         %d\n", sblock->s_state); 
  printf ("s_zones:%d\n", sblock->s_zones);
}

unsigned int get_imap_bit (int i) {
  // Aufgabe d)
  unsigned char block[1024];
  unsigned int byte;
  readblock (2, (unsigned char*)&block);    // Inode-Map = Block 2
    // vollstaendige Loesung: evtl. mehr als 1 Block bearbeiten
  byte = block[i/8];
  return (byte >> (i%8)) % 2;  
};

unsigned int get_zmap_bit (int i) {
  // Aufgabe d)
  unsigned char block[1024];
  unsigned int byte;
  readblock (3, (unsigned char*)&block);    // Zone-Map = Block 3
    // vollstaendige Loesung: evtl. mehr als 1 Block bearbeiten
  byte = block[i/8];
  return (byte >> (i%8)) % 2;  
};

void show_inode_bitmap () {
  // Aufgabe c), benutzt Loesung zu d)
  int no_inodes = 480;  // korrekte Loesung: S-Block auswerten
  int i, j;
  for (i = 0; i<no_inodes; i+=64) {
    // print 64 entries
    printf ("%03d : ", i);  // Position
    for (j = i; j<i+64 && j<no_inodes; j++) {
      printf ("%d", get_imap_bit (j));
      if ((j%8)==7) printf (" ");
    };
    printf ("\n");
  };
  printf ("Belegte Inodes: ");
  for (i = 0; i<480; i++) {
    if (get_imap_bit (i)==1) printf ("%d, ", i);
  };
  printf ("\n");
}

void show_zone_bitmap () {
  // Aufgabe c), benutzt Loesung zu d)
  int no_zones = 1440;  // korrekte Loesung: S-Block auswerten
  unsigned int i, j;
  for (i = 0; i<no_zones-33; i+=64) {
    // -33: Liste ist nur 1440-33 Zeichen lang
    // korrekte Loesung: rausfinden, wie viele Bloecke Datenbloecke sind
    // print 64 entries
    printf ("%04d : ", i+33);  // Position, 1. Eintrag fuer Block 33
    for (j = i; j<i+64 && j<no_zones-33; j++) {
      // print entry j
      printf ("%d", get_zmap_bit (j));
      if ((j%8)==7) printf (" ");
    };
    printf ("\n");
  };
  printf ("Belegte Zones: ");
  for (i = 0; i<1440-33; i++) {
    if (get_zmap_bit (i)==1) printf ("%d, ", i+33);  // +33 !
  };
  printf ("\n");
}

void set_clear_imap_bit (int i, int value) {
  // Aufgabe d), eine Funktion fuer set und clear
  unsigned char block[1024];
  unsigned int byte;
  readblock (2, (unsigned char*)&block);    // Inode-Map = Block 2
    // vollstaendige Loesung: evtl. mehr als 1 Block bearbeiten
  byte = block[i/8];
  if (value==0) {
    // Clear bit
    byte = byte & (255 - (1<<(i%8)));
  } else {
    // Set bit
    byte = byte | 1<<(i%8);
  }; 
  block[i/8] = byte;
  writeblock (2, (unsigned char*)&block);  
};

void set_clear_zmap_bit (int i, int value) {
  // Aufgabe d), eine Funktion fuer set und clear
  unsigned char block[1024];
  unsigned int byte;
  readblock (3, (unsigned char*)&block);    // Zone-Map = Block 3
    // vollstaendige Loesung: evtl. mehr als 1 Block bearbeiten
  byte = block[i/8];
  if (value==0) {
    // Clear bit
    byte = byte & (255 - (1<<(i%8)));
  } else {
    // Set bit
    byte = byte | 1<<(i%8);
  }; 
  block[i/8] = byte;
  writeblock (3, (unsigned char*)&block);  
};

void set_imap_bit (int i)   { set_clear_imap_bit (i, 1); };
void clear_imap_bit (int i) { set_clear_imap_bit (i, 0); };
void set_zmap_bit (int i)   { set_clear_zmap_bit (i, 1); };
void clear_zmap_bit (int i) { set_clear_zmap_bit (i, 0); };


int request_inode () {
  // Aufgabe e)
  int no_inodes = 480;  // korrekte Loesung: S-Block auswerten
  int i;
  for (i = 0; i < no_inodes; i++) {
    if (get_imap_bit (i) == 0) {
      // Treffer!
      set_imap_bit (i);   // als belegt markieren
      return i;
    }
  }
  return -1; // nichts gefunden
};

int request_block () {
  // Aufgabe e)
  int no_zones = 1440;  // korrekte Loesung: S-Block auswerten
  int i;
  for (i = 0; i < no_zones; i++) {
    if (get_zmap_bit (i) == 0) {
      // Treffer!
      set_zmap_bit (i);   // als belegt markieren
      return i+33;        // Blocknr.: i+33 !!
    }
  }
  return -1; // nichts gefunden
};

int main () {
  printf ("Super Block:\n");     show_superblock ();
  printf ("\nInode Bitmap:\n");  show_inode_bitmap ();
  printf ("\nZone Bitmap:\n");   show_zone_bitmap ();
  
  clear_imap_bit (3); set_imap_bit (20);  // clear und set
  printf ("\nInode Bitmap:\n");  show_inode_bitmap ();
  set_imap_bit (3); clear_imap_bit (20);  // rueckgaengig machen

  clear_zmap_bit (5); set_zmap_bit (100);  // clear und set
  printf ("\nZone Bitmap:\n");  show_zone_bitmap ();
  set_zmap_bit (5); clear_zmap_bit (100);  // rueckgaengig machen

  int ino = request_inode ();
  printf ("\nrequest_inode() -> %d\n", ino);
  clear_imap_bit (ino);  // rueckgaengig machen

  int blockno = request_block ();
  printf ("request_block() -> %d\n", blockno);
  clear_zmap_bit (blockno-33);  // rueckgaengig machen; -33 !!
};
