Gestore in C




/* file di header base-function.h
 * 
 * dichiarazione dei prototipi delle funzioni di base per la gestione
 * del vfs
 */
 
// inclusioni fondamentali
#include "const.h"
#include "coderr.h"
#include "structures.h"
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

// prototipo per la create_vfs, funzione che crea e inizializza il vfs
int create_vfs(int num_files);

// prototipo per la allocate_block, funzione per occupare un blocco e
// legarla all'inode
int allocate_block(I_TYPE itype, I_NODE *inode);

// prototipo per la deallocate_block, funzione per liberare spazio nel
// vfs 
int deallocate_block(I_NODE *inode, unsigned int i_node_offset);

// prototipo per la get_i_node, funzione per risalire all'inode 
// associato ad uno specifico path
int get_i_node(char *path, I_NODE *inode);

// prototipo per la make_named_socket, funzione per creare una socket
// con nome
int make_named_socket (const char *filename);

// prototipo per la error_handler, funzione per creare il messaggio
// di errore da inviare poi al server
void error_handler(char *error, char **message);

/* fine file */




/* file di intestazione coderr.h
 *
 * definizione di tutti i codici di errore necessari per gestire 
 * tutti i problemi che si possono verificare
 */

// DEFINIZIONE CODICI DI ERRORE

// uscita con successo
#define   EXIT_SUCCESS        0

// errore generico
#define   GEN_FAIL            -1

// errore nella creazione del vfs, troppi pochi files
#define   NUM_FILES_ERROR     -6


// DEFINIZIONE MESSAGGI DI ERRORE E ALTRO

#define   NUM_FILES_ERROR_MESSAGE \
          "VFS must have 3 or more files\n"

#define   GET_INODE_ERROR_MESSAGE \
          "Error in i-node resolution"

#define   VOID_DIR_MESSAGE \
          "0 files 0 directory"

#define   OPEN_ERROR_MESSAGE \
          "Cannot access file system"

#define   INVALID_PATH_MESSAGE \
          "Not such a path"

#define   NOT_A_DIRECTORY_ERROR_MESSAGE \
          "Not a directory"

#define   FULL_DIR_ERROR_MESSAGE \
          "Directory full"

#define   VFS_INTERNAL_ERROR_MESSAGE \
          "File system internal error"

#define   FULL_DISK_ERROR_MESSAGE \
          "Virtual disk full"

#define   PATH_ALREADY_EXIST_ERROR_MESSAGE \
          "Specified path already exist"

#define   NOT_ENOUGH_MEMORY_ERROR_MESSAGE \
          "Not enough memory"

#define   INVALID_PATH_OS_FS_ERROR_MESSAGE \
          "Invalid path on the OS file system"

#define   CANNOT_WRITE_OS_FS_ERROR_MESSAGE \
          "Can't write on the OS file system"

#define   CANNOT_READ_OS_FS_ERROR_MESSAGE \
          "Can't read from the OS file system"

#define   FILE_SIZE_EXCEED_ERROR_MESSAGE \
          "File to be copied too large"

#define   CANNOT_CP_DIRECTORY_ERROR_MESSAGE \
          "Cannot copy a directory"

#define   CANNOT_CP_INVALID_FILE_ERROR_MESSAGE \
          "Cannot copy anything but links or r-files"

#define   CANNOT_OVERWRITE_FILE_ERROR_MESSAGE \
          "Cannot overwrite file/directory"

#define   CANNOT_OVERWRITE_OS_FILE_ERROR_MESSAGE \
          "Cannot overwrite file on the OS file system"

#define   INVALID_TARGET_PATH_ERROR_MESSAGE \
          "Invalid link target"

#define   CANNOT_LINK_DIR_ERROR_MESSAGE \
          "Hard link not allowed for directory"

#define   INVALID_LINK_PATH_ERROR_MESSAGE \
          "Invalid link name"
          
#define   CANNOT_PURGE_NOT_EMPTY_DIR_ERROR_MESSAGE \
          "Directory is not empty"

#define   CANNOT_DELETE_DIR_ERROR_MESSAGE \
          "Cannot remove: is a directory"
          
#define   CANNOT_UNDEL_EXISTING_FILE_ERROR_MESSAGE \
          "Cannot undel existing file"
          
/* fine file */




/* file di header const.h
 *
 * definizione di alcune utili costanti
 */

// dimensione del blocco
#define  BLOCK_SIZE  262144

/* dimensione dell'i-node (ricordando che la struttura I_NODE e' formata
 * da 5 interi e dalla struttura I_TYPE, che ha la dimensione di un int)
 */
#define  I_NODE_SIZE  6*sizeof(int)

// lunghezza massima del nome di un file/directory
#define  FILE_NAME_SIZE  256

// numero massimo di entry per una directory
#define  NUM_DIR_ENTRY BLOCK_SIZE / (FILE_NAME_SIZE * sizeof(char) \
                           + sizeof(int))

// file utilizzato per creare vfs
#define  PATH "/tmp/vfs.tmp"

// file utilizzato per creare vfs
#define  SOCK_PATH "/tmp/vfs.sock"

// tempo dopo il quale la socket, se non riceve dati, viene terminata
#define  SOCKET_TIMEOUT_VAL   7

// numero di file che il vfs potra' contenere
#define  VFS_NUM_FILES  50

// posizione del primo i-node (ossia la /)
#define  ROOT_DIR_OFFSET  sizeof(int)+sizeof(int)

/* fine file */




/* file di header structures.h
 * 
 * definizione delle strutture dati fondamentali per la
 * gestione del VFS
 */

// enumerazione per definire il tipo a cui poi puntera' un INODE
typedef enum {
   RFILE, // regular file
   DIR    // directory
} I_TYPE;

// definizione della struttura di un INODE
typedef struct {
   // tipo di oggetto puntato
   I_TYPE type;
   // timestamp dell'unix epoch
   unsigned int timedate;
   // contatore di condivisione (per gli hard link)
   unsigned int sharing;
   // offset al blocco in cui il file e contenuto  
   unsigned int block;
   // dimensione in byte dell'oggetto definito (in caso di
   // file), o il numero di elementi contenuti (in caso di
   // directory)
   unsigned int size;
   // offset che punta al prossimo i-node libero se il presente non
   // e' utilizzato
   unsigned int nextfree;
} I_NODE;

// definizione di una directory entry
typedef struct {
   // rappresenta il nome dell'oggetto puntato
   char name[FILE_NAME_SIZE];
   // puntatore all'inode
   int i_node;
} DIRECTORY_ENTRY;

// definizione di un array per una intera directory
typedef DIRECTORY_ENTRY DIRECTORY[NUM_DIR_ENTRY];

/* fine file */




/* file sorgente allocate-block.c
 *
 * definizione della funzione allocate_block che si occupera di 
 * collegare il primo blocco libero al primo inode libero
 */
 
#include "base-functions.h"

int allocate_block(I_TYPE itype, I_NODE *inode) {
   // variabili di appoggio
   int fli,flb,nfli,nflb,vfs;
   I_NODE *in = (I_NODE *)malloc(sizeof(I_NODE)); 
   
   // si apre il file in lettura e si gestiscono eventuali errori
   if ((vfs = open(PATH,O_RDONLY)) == -1) {
      free(in); // libero la memoria allocata per l'inode d'appoggio
      return GEN_FAIL;
   }
      
   // da qui si leggono fli e flb e si gestiscono gli errori  
   if(read(vfs,&fli,sizeof(int)) < sizeof(int)) { // accedo ad fli
      close(vfs);
      free(in); // libero la memoria allocata per l'inode d'appoggio
      return GEN_FAIL;
   }
   
   // se trovo che fli e' uguale a 0 vuol dire che il file system e'
   // pieno
   if (fli == 0) {
      close(vfs);
      free(in); // libero la memoria allocata per l'inode d'appoggio
      return GEN_FAIL;
   }
   
   // leggo il blocco libero da associare all'inode
   if(read(vfs,&flb,sizeof(int)) < sizeof(int)) { // accedo ad flb
      close(vfs);
      return GEN_FAIL;
   }
   
   // se trovo flb uguale a 0 ho il file system pieno
   if (flb == 0) {
      close(vfs);
      free(in); // libero la memoria allocata per l'inode d'appoggio
      return GEN_FAIL;
   }
   
   // si va a leggere dentro l'inode libero l'offset del prossimo
   // inode libero
   if (lseek(vfs,fli,SEEK_SET) == -1) {
      free(in); // libero la memoria allocata per l'inode d'appoggio
      close(vfs);
      return GEN_FAIL;
   }
   
   // lettura dell'i-node
   if(read(vfs,in,I_NODE_SIZE) < I_NODE_SIZE) {
      free(in); // libero la memoria allocata per l'inode d'appoggio
      close(vfs);
      return GEN_FAIL;
   }
   
   nfli = in->nextfree; // trovo il prossimo i-node libero
   free(in); // libero la memoria allocata per l'inode d'appoggio
   
   // si va a leggere dentro il blocco libero l'offset del prossimo
   // blocco libero
   if(lseek(vfs,flb,SEEK_SET) == -1) {
      close(vfs);
      return GEN_FAIL;
   }
      
   // leggo l'indirizzo
   if(read(vfs,&nflb,sizeof(int)) < sizeof(int)) {
      close(vfs);
      return GEN_FAIL;
   }
    
   close(vfs);
   
   // configuro l'inode in uscita in modo da essere pronto
   inode->type = itype;
   inode->timedate = time(NULL);
   inode->sharing = 1;
   inode->block = flb;
   inode->size = 0;
   inode->nextfree = 0;
   
   // si apre il file in scrittura e si gestiscono gli errori
   vfs = open(PATH,O_WRONLY);
   if(vfs == -1)
      return GEN_FAIL;
   
   // scrivo nella prima parte del VFS l'offset del prossimo i-node
   // libero da usare
   if(write(vfs,(int *)&nfli,sizeof(int)) < sizeof(int)) {
      close(vfs);
      return GEN_FAIL;
   }
   
   // scrivo nel byte successivo a fli l'offset del prossimo blocco
   // libero
   if(write(vfs,(int *)&nflb,sizeof(int)) < sizeof(int)) {
      close(vfs);
      return GEN_FAIL;
   }
   
   // mi sposto sull'inode da utilizzare      
   if(lseek(vfs,fli,SEEK_SET) == -1) {
      close(vfs);
      return GEN_FAIL;
   }
   
   // salvo le modifiche apportate all'inode
   if(write(vfs,inode,I_NODE_SIZE) < I_NODE_SIZE) {
      close(vfs);
      return GEN_FAIL;
   }
   
   close(vfs);
   
   // tutto e andato bene
   return fli;
}

/* fine file */




/* file sorgente cp_fs2vd.c
 *
 * definizione della funzione che si occupa di effettuare le copie
 * da FS a VFS
 */

#include <sys/stat.h> 
#include <string.h>
#include "base-functions.h"

// funzione di appoggio per copiare il file sul VFS
int insert_file_fs2vd(char *path_fs, int size);

// copia un file contenuto in un file system esterno all'interno del VFS
int cp_fs2vd(char * path_fs, char * path_vd, char **message) {
   // struttura per leggere le informazioni sul file reale
   struct stat temp;
   // descrittore per i file
   int dsc;
   // valore del puntatore al primo inode libero
   int fli;
   // parte sinistra del path di destinazione (escluso l'ultimo token)
   char *safe_dest_path;
   // parte destra del path di destinazione (ultimo token)
   char *terminal_name;
   // parte destra del path sorgente (ultimo token)
   char *terminal_source;
   // inode per la allocate
   I_NODE temp_inode;
   // temporanei per le seek nella directory entry
   int offset_block;
   // variabili d'appoggio
   int k,found_duplicate;
   DIRECTORY_ENTRY temp_dir_entry;
   int allocated_offset_inode;
   // offset per l'i-node della directory entry
   int offset_inode_directory;
   
   // ricaviamo le varie informazioni riguardanti il file reale
   // e quindi controlliamo anche se esiste
   if(stat(path_fs, &temp) == GEN_FAIL) {
      error_handler(INVALID_PATH_OS_FS_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se il path_fs e' una directory rispondo con un errore
   if(S_ISDIR(temp.st_mode)) {
      error_handler(CANNOT_CP_DIRECTORY_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se il path_fs e' un file non copiabile (es. socket, device, ecc.)
   if(!(S_ISREG(temp.st_mode) || S_ISLNK(temp.st_mode))) {
      error_handler(CANNOT_CP_INVALID_FILE_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // apro il file per leggere fli
   if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
      
   // leggiamo il valore di fli   
   if (read(dsc, &fli, sizeof(int)) < sizeof(int)) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      close(dsc);
      return GEN_FAIL;
   }
   
   // si controlla il valore di fli: se uguale a zero significa 
   // che il disco e' pieno
   if(fli == 0){
      error_handler(FULL_DISK_ERROR_MESSAGE,message);
      close(dsc);
      return GEN_FAIL;     
   }
   
   // chiudo il file
   close(dsc);
   
   // dobbiamo prima verificare che il file che copiamo nel nostro disco
   // sia di dimensione massima pari a quella definita in const.h come
   // BLOCK_SIZE
   if(temp.st_size > BLOCK_SIZE) {
      error_handler(FILE_SIZE_EXCEED_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se il path_vd non contiene barre (e' malformato) usciamo
   if((terminal_name = strrchr(path_vd,'/')) == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se la strrchr va a buon fine con il ++ eliminiamo la barra 
   // iniziale che non ci interessa
   terminal_name++;
   
   // inserisco in safe_dest_path la parte sinistra
   safe_dest_path = (char*)malloc((terminal_name - path_vd)+1);
   safe_dest_path[0] = '\0';
   strncat(safe_dest_path, path_vd,(size_t)((terminal_name - path_vd)));
   // controllo che il safe_dest_path sia valido nel vfs
   if((offset_inode_directory=get_i_node(safe_dest_path,&temp_inode)) \
                                                          == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      free(safe_dest_path);
      return GEN_FAIL;
   }
   
   // libero la memoria
   free(safe_dest_path);
   
   // controllo se si tratta di una directory: non si puo' copiare un
   // file 'dentro' un file
   if(temp_inode.type == RFILE) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se terminal_name e' vuoto inserisco terminal_source
   terminal_source = strrchr(path_fs,'/')+1;
   if(strcmp(terminal_name,"") == 0) {
      strncat(terminal_name,terminal_source,256);
   }
   
   // la directory contiene gia' una entry terminal_name?
   if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // mi sposto alla posizione del blocco della directory table
   // non gestisco l'errore perche' non puo' fallire
   offset_block = temp_inode.block;
   
   if(lseek(dsc,offset_block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(dsc);
      return GEN_FAIL;
   }
   
   // controllo se esiste gia' una entry nella directory
   found_duplicate = 0;
   for(k = 0; k < NUM_DIR_ENTRY; k++) {
      if(read(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
         < sizeof(DIRECTORY_ENTRY)) {
         close(dsc);
         return GEN_FAIL;
      }
      
      if(strcmp(temp_dir_entry.name,terminal_name) == 0) {
         found_duplicate = 1;
         break;
      }
   }
   
   // chiudo il file
   close(dsc);
   
   // se non c'e' la entry
   if(found_duplicate == 0) {
      // la directory e' piena?
      if(temp_inode.size >= NUM_DIR_ENTRY) {
         error_handler(FULL_DIR_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // chiamo la funzione che alloca il blocco per il nuovo file, e 
      // lo scrive nel vfs, mi restituisce l'offset all'i-node da
      // aggiungere alla directory 
      if((allocated_offset_inode = insert_file_fs2vd(path_fs, \
                                           temp.st_size)) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // aggiorno l'i-node della directory entry incrementandone il size
      temp_inode.size++;
      
      // preparo la nuova entry per la directory
      strcpy(temp_dir_entry.name,terminal_name);
      temp_dir_entry.i_node = allocated_offset_inode;
      
      // apro il vfs
      if((dsc = open(PATH, O_WRONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione della directory entry
      if(lseek(dsc,offset_block + (temp_inode.size - 1) \
                  * sizeof(DIRECTORY_ENTRY),SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // aggiungo la nuova entry (nome e size)
      if(write(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione dell'i-node della directory
      if(lseek(dsc,offset_inode_directory,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // a questo punto posso aggiornare l'inode
      if(write(dsc,&temp_inode,I_NODE_SIZE) < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }

      // chiudo il file
      close(dsc);
      
      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';     
      return EXIT_SUCCESS;  
      
   } else {
   // se invece ho un duplicato
      // ottengo il suo i-node per verificare se e' una directory
      if((offset_inode_directory = get_i_node(path_vd, &temp_inode)) \
                                                         == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // se non e' una directory errore
      if(temp_inode.type != DIR) {
         error_handler(CANNOT_OVERWRITE_FILE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
            
      // la directory contiene gia' una entry terminal_source?
      if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
   
      // mi sposto alla posizione del blocco della directory table
      // non gestisco l'errore perche' non puo' fallire
      offset_block = temp_inode.block;
   
      if(lseek(dsc,offset_block,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
   
      // controllo se esiste gia' una entry nella directory
      found_duplicate = 0;
      for(k = 0; k < NUM_DIR_ENTRY; k++) {
         if(read(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
            close(dsc);
            return GEN_FAIL;
         }
      
         if(strcmp(temp_dir_entry.name,terminal_source) == 0) {
            found_duplicate = 1;
            break;
         }
      }
      
      // chiudo il file
      close(dsc);
      
      // se esiste gia' la entry non si puo' sovrascrivere
      if(found_duplicate == 1) {
         error_handler(CANNOT_OVERWRITE_FILE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // c'e' spazio nella directory?
      if(temp_inode.size >= NUM_DIR_ENTRY) {
         error_handler(FULL_DIR_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // se c'e' spazio copio il file e aggiungo la entry
      // chiamo la funzione che alloca il blocco per il nuovo file, e
      // lo scrive nel vfs, mi restituisce l'offset all'i-node da
      // aggiungere alla directory 
      if((allocated_offset_inode = insert_file_fs2vd(path_fs, \
                                          temp.st_size)) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // aggiorno l'i-node della directory entry incrementandone il size
      temp_inode.size++;
      
      // preparo la nuova entry per la directory
      strcpy(temp_dir_entry.name,terminal_source);
      temp_dir_entry.i_node = allocated_offset_inode;
      
      // apro il vfs
      if((dsc = open(PATH, O_WRONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione della directory entry
      if(lseek(dsc,temp_inode.block + (temp_inode.size - 1) \
                      * sizeof(DIRECTORY_ENTRY),SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // aggiungo la nuova entry (nome e size)
      if(write(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                            < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione dell'i-node della directory
      if(lseek(dsc,offset_inode_directory,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // a questo punto posso aggiornare l'inode
      if(write(dsc,&temp_inode,I_NODE_SIZE) \
               < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);       
         close(dsc);
         return GEN_FAIL;
      }
      
      // chiudo il file
      close(dsc);

      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      return EXIT_SUCCESS; 
   }
}

// restituiamo come int l'offset
int insert_file_fs2vd(char *path_fs,int size) {
   int dsc, offset_temp_inode;
   char *swap;
   I_NODE temp_inode;
   
   // a questo punto possiamo leggere il file dal file system reale:
   // inizializzo il descrittore per accedere ai dati, in sola lettura
   if((dsc = open(path_fs, O_RDONLY)) == GEN_FAIL) {
      return GEN_FAIL;
   }
   
   // alloco una memoria sufficiente per contenere il file puntato 
   // da path_fs
   if((swap = (char *)malloc(size)) == NULL) {
      close(dsc);
      return GEN_FAIL;
   } 

   // leggo per intero il file coinvolto nel trasferimento dal path_fs
   if(read(dsc,swap,size) < size) {
     close(dsc);
     free(swap);
     return GEN_FAIL;
   }
   
   // chiudo il file path_fs
   close(dsc);
   
   // alloco un nuovo blocco libero
   if((offset_temp_inode = allocate_block(RFILE, &temp_inode)) \
                                                         == GEN_FAIL) {
     free(swap);
     return GEN_FAIL;
   }
   
   // scrivo la size del file
   temp_inode.size = size;
   
   // aggiorno l'i-node:
   
   // accedo al file system virtuale
   if((dsc = open(PATH,O_WRONLY)) == GEN_FAIL) {
      free(swap);
      return GEN_FAIL;
   }
   
   // mi sposto all'offset dell'i-node nel vfs
   if(lseek(dsc,offset_temp_inode,SEEK_SET) == GEN_FAIL) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }

   // scrivo l'i-node
   if(write(dsc,&temp_inode,I_NODE_SIZE) < I_NODE_SIZE) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }
   
   // mi sposto nel punto corrispondente al blocco in cui devo scrivere
   // il file
   if(lseek(dsc,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }

   // a questo punto posso scrivere il blocco
   if(write(dsc,swap,size) < size) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }
   
   // libero la memoria
   free(swap);
   
   // chiudo il file path_vd
   close(dsc);
   
   return offset_temp_inode;
}

/* fine file */




/* file sorgente cp_vd2fs.c
 *
 * definizione della funzione che si occupa di effettuare le copie
 * da VFS a FS
 */

#include <sys/stat.h> 
#include <string.h>
#include "base-functions.h"

int cp_vd2fs(char * path_vd, char * path_fs, char **message) {
   // inode per la lettura del file dal vfs
   I_NODE temp_inode;
   // descrittore del file
   int dsc;
   // buffer nel quale leggere il file
   char *swap;
   // variabili d'appoggio
   char *aux;
   // nuovo path
   char *new_path;
   // ultimo token di path_fs
   char *terminal_source;
   // parte sinistra del path_fs
   char *safe_dest_path;
   // struttura per l'accesso ai file sull'OS FS
   struct stat temp;

   // controllo che il path sia ben formato
   aux = strrchr(path_vd,'/');
   if(aux == NULL) {
      error_handler(OPEN_ERROR_MESSAGE,message);
      return GEN_FAIL;
   }

   // controllo che il path esista e che venga correttamente risolto
   if(get_i_node(path_vd,&temp_inode) == GEN_FAIL) {
      error_handler(GET_INODE_ERROR_MESSAGE,message);
      return GEN_FAIL;
   }

   // controllo che sia un regular-file
   if(temp_inode.type == DIR) {
      error_handler(CANNOT_CP_INVALID_FILE_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // alloco una memoria sufficiente per poter leggere il file
   if((swap = (char *)malloc(sizeof(char) * temp_inode.size)) == NULL) {
      error_handler(NOT_ENOUGH_MEMORY_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // apro il file nel vd in modo da leggerlo
   if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      free(swap);
      return GEN_FAIL;
   }

   // mi sposto nel punto in cui e' memorizzato il file nel vd
   if(lseek(dsc,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      free(swap);
      close(dsc);
      return GEN_FAIL;
   }

   // a questo punto posso leggere dal vd il file da copiare
   if(read(dsc,swap,temp_inode.size) < temp_inode.size) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      free(swap);
      close(dsc);
      return GEN_FAIL;
   }

   // chiudo il file
   close(dsc);

   // verifico se il path passato per la scrittura sia ben formato
   terminal_source = strrchr(path_fs,'/');
   if(terminal_source == NULL) {
      error_handler(INVALID_PATH_OS_FS_ERROR_MESSAGE, message);
      free(swap);
      return GEN_FAIL;
   }


   if(stat(path_fs, &temp) != GEN_FAIL) {
      // se il path nell'OS FS esiste:

      // se non si tratta di una cartella
      if(!S_ISDIR(temp.st_mode)) {
         error_handler(CANNOT_OVERWRITE_OS_FILE_ERROR_MESSAGE, message);
         free(swap);
         return GEN_FAIL;
      }

      // se l'ultimo carattere e' una '/' lo sostituisco con \0
      if((terminal_source - path_fs) == strlen(path_fs) - 1) {
         path_fs[(terminal_source - path_fs)] = '\0';
         terminal_source = strrchr(path_fs,'/');
      }

      new_path = (char*)malloc(strlen(path_fs)+FILE_NAME_SIZE+2);
      new_path[0] = '\0';
      
      // se si tratta di una cartella aggiungo "/aux"
      aux = strrchr(path_vd,'/')+1;
      strcpy(new_path,path_fs);
      strncat(new_path,"/",1);
      strncat(new_path,aux,FILE_NAME_SIZE+1);

      // se esiste il nuovo path errore
      if(stat(new_path, &temp) != GEN_FAIL) {
         error_handler(CANNOT_OVERWRITE_OS_FILE_ERROR_MESSAGE, message);
         free(swap);
         free(new_path);
         return GEN_FAIL;
      }

      // altrimenti creo il file nel nuovo path
      if((dsc = open(new_path,O_WRONLY | O_CREAT, 0644)) == GEN_FAIL) {
         error_handler(INVALID_PATH_OS_FS_ERROR_MESSAGE, message);
         free(swap);
         free(new_path);
         return GEN_FAIL;
      }

      // scrivo il file sul file system reale
      if(write(dsc,swap,temp_inode.size) < temp_inode.size) {
         error_handler(CANNOT_WRITE_OS_FS_ERROR_MESSAGE, message);
         free(swap);
         free(new_path);
         close(dsc);
         return GEN_FAIL;
      }

      // chiudo il file
      close(dsc);

      // libero la memoria
      free(swap);
      free(new_path);

      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      return EXIT_SUCCESS;
   } else {
      // se il path nell'OS FS non esiste:

      // se l'ultimo carattere e' una '/' il path e' invalido
      if((terminal_source - path_fs) == strlen(path_fs) - 1) {
         //path_fs[(terminal_source - path_fs)] = '\0';
         //terminal_source = strrchr(path,'/');
         error_handler(INVALID_PATH_OS_FS_ERROR_MESSAGE, message);
         free(swap);
         return GEN_FAIL;
      }

      // creo una stringa con la parte sinistra del path_fs
      safe_dest_path = (char*)malloc(strlen(path_fs)+1);
      safe_dest_path[0] = '\0';
      strcpy(safe_dest_path,path_fs);
      safe_dest_path[terminal_source-path_fs] = '\0';


      // controllo se esiste la parte sx del path
      if(stat(safe_dest_path, &temp) == GEN_FAIL) {
         error_handler(INVALID_PATH_OS_FS_ERROR_MESSAGE, message);
         free(swap);
         free(safe_dest_path);
         return GEN_FAIL;
      }

      // se non si tratta di una cartella errore
      if(!S_ISDIR(temp.st_mode)) {
         error_handler(CANNOT_OVERWRITE_OS_FILE_ERROR_MESSAGE, message);
         free(swap);
         free(safe_dest_path);
         return GEN_FAIL;
      }

      // altrimenti creo il file nel nuovo path
      if((dsc = open(path_fs,O_WRONLY | O_CREAT, 0644)) == GEN_FAIL) {
         error_handler(INVALID_PATH_OS_FS_ERROR_MESSAGE, message);
         free(swap);
         free(safe_dest_path);
         return GEN_FAIL;
      }

      // scrivo il file sul file system reale
      if(write(dsc,swap,temp_inode.size) < temp_inode.size) {
         error_handler(CANNOT_WRITE_OS_FS_ERROR_MESSAGE, message);
         free(swap);
         free(safe_dest_path);
         close(dsc);
         return GEN_FAIL;
      }

      // chiudo il file
      close(dsc);

      // libero la memoria
      free(swap);
      free(safe_dest_path);

      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      return EXIT_SUCCESS;
   }
}

/* fine file */




/* file sorgente cp_vd2vd.c
 *
 * definizione della funzione che si occupa di effettuare le copie
 * all'interno del disco virtuale stesso
 */

#include <sys/stat.h>
#include <string.h>
#include "base-functions.h"

// funzione di appoggio per copiare il file sul VFS
int insert_file_vd2vd(char *path_dest, char *swap, int size);

int cp_vd2vd(char *path_source, char *path_dest, char **message) {
   // i-node per il file sorgente
   I_NODE source_inode;
   // swap per la lettura scrittura
   char *swap;
   // il nome del file (parte destra del path)
   char *terminal_name;
   // variabili di appoggio
   int fli;
   int dsc;
   char *safe_dest_path;
   int offset_inode_directory;
   I_NODE temp_inode;
   char *terminal_source;
   int offset_block;
   int found_duplicate;
   int k;
   DIRECTORY_ENTRY temp_dir_entry;
   int allocated_offset_inode;
   
   // accedo al file system virtuale
   if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      free(swap);
      return GEN_FAIL;
   }
   
   // in path_source ho la sorgente da ricercare nel vfs
   if(get_i_node(path_source,&source_inode) == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se il path_source e' una directory rispondo con un errore
   if(source_inode.type == DIR) {
      error_handler(CANNOT_CP_DIRECTORY_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // alloco una memoria sufficiente per contenere il file puntato da
   // path
   if ((swap = (char *)malloc(sizeof(char)*source_inode.size)) == NULL) {
      error_handler(NOT_ENOUGH_MEMORY_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // mi sposto sul blocco giusto nel file system virtuale
   if(lseek(dsc,source_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      free(swap);
      return GEN_FAIL;
   }
   
   // leggo per intero il file coinvolto nel trasferimento dal vfs
   if(read(dsc,swap,source_inode.size) < source_inode.size) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }
   
   // il primo test che dobbiamo effettuare e' che il disco virtuale non 
   // sia pieno, controllando che il valore di fli non sia pari a 0,
   // pertanto ci spostiamo sul blocco giusto nel file system virtuale
   if(lseek(dsc,0,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      free(swap);
      return GEN_FAIL;
   }
   
   // leggiamo il valore di fli
   if (read(dsc, &fli, sizeof(int)) < sizeof(int)) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      close(dsc);
      return GEN_FAIL;
   }
   
   // verifichiamo il valore di fli
   if(fli == 0){
      error_handler(FULL_DISK_ERROR_MESSAGE,message);
      close(dsc);
      return GEN_FAIL;
   }

   close(dsc);

   // se il path_dest non contiene barre (e' malformato) usciamo
   if((terminal_name = strrchr(path_dest,'/')) == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se la strrchr va a buon fine con il ++ eliminiamo la barra 
   // iniziale che non ci interessa
   terminal_name++;
   
   // inserisco in safe_dest_path la parte sinistra
   safe_dest_path = (char*)malloc((terminal_name - path_dest)+1);
   safe_dest_path[0] = '\0';
   strncat(safe_dest_path, path_dest,(size_t)((terminal_name - \
                                     path_dest)));
   // controllo che il safe_dest_path sia valido nel vfs
   if((offset_inode_directory=get_i_node(safe_dest_path,&temp_inode)) \
                                                          == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      free(safe_dest_path);
      return GEN_FAIL;
   }
   
   // libero la memoria
   free(safe_dest_path);
   
   // controllo se si tratta di una directory: non si puo' copiare un
   // file 'dentro' un file
   if(temp_inode.type == RFILE) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se terminal_name e' vuoto inserisco terminal_source
   terminal_source = strrchr(path_source,'/')+1;
   if(strcmp(terminal_name,"") == 0) {
      strncat(terminal_name,terminal_source,256);
   }
   
   // la directory contiene gia' una entry terminal_name?
   if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // mi sposto alla posizione del blocco della directory table
   // non gestisco l'errore perche' non puo' fallire
   offset_block = temp_inode.block;
   
   if(lseek(dsc,offset_block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(dsc);
      return GEN_FAIL;
   }
   
   // controllo se esiste gia' una entry nella directory
   found_duplicate = 0;
   for(k = 0; k < NUM_DIR_ENTRY; k++) {
      if(read(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
         < sizeof(DIRECTORY_ENTRY)) {
         close(dsc);
         return GEN_FAIL;
      }
      
      if(strcmp(temp_dir_entry.name,terminal_name) == 0) {
         found_duplicate = 1;
         break;
      }
   }
   
   // chiudo il file
   close(dsc);
   
   // se non c'e' la entry
   if(found_duplicate == 0) {
      // la directory e' piena?
      if(temp_inode.size >= NUM_DIR_ENTRY) {
         error_handler(FULL_DIR_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // chiamo la funzione che alloca il blocco per il nuovo file, e 
      // lo scrive nel vfs, mi restituisce l'offset all'i-node da
      // aggiungere alla directory 
      if((allocated_offset_inode = insert_file_vd2vd(path_source, swap,\
                                   source_inode.size)) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // aggiorno l'i-node della directory entry incrementandone il size
      temp_inode.size++;
      
      // preparo la nuova entry per la directory
      strcpy(temp_dir_entry.name,terminal_name);
      temp_dir_entry.i_node = allocated_offset_inode;
      
      // apro il vfs
      if((dsc = open(PATH, O_WRONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione della directory entry
      if(lseek(dsc,offset_block + (temp_inode.size - 1) \
                  * sizeof(DIRECTORY_ENTRY),SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // aggiungo la nuova entry (nome e size)
      if(write(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione dell'i-node della directory
      if(lseek(dsc,offset_inode_directory,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // a questo punto posso aggiornare l'inode
      if(write(dsc,&temp_inode,I_NODE_SIZE) < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }

      // chiudo il file
      close(dsc);
      
      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      return EXIT_SUCCESS; 
      
   } else {
   // se invece ho un duplicato
      // ottengo il suo i-node per verificare se e' una directory
      if((offset_inode_directory = get_i_node(path_dest, &temp_inode)) \
                                                         == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // se non e' una directory errore
      if(temp_inode.type != DIR) {
         error_handler(CANNOT_OVERWRITE_FILE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
            
      // la directory contiene gia' una entry terminal_source?
      if((dsc = open(PATH,O_RDONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
   
      // mi sposto alla posizione del blocco della directory table
      // non gestisco l'errore perche' non puo' fallire
      offset_block = temp_inode.block;
   
      if(lseek(dsc,offset_block,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
   
      // controllo se esiste gia' una entry nella directory
      found_duplicate = 0;
      for(k = 0; k < NUM_DIR_ENTRY; k++) {
         if(read(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
            close(dsc);
            return GEN_FAIL;
         }
      
         if(strcmp(temp_dir_entry.name,terminal_source) == 0) {
            found_duplicate = 1;
            break;
         }
      }
      
      // chiudo il file
      close(dsc);
      
      // se esiste gia' la entry non si puo' sovrascrivere
      if(found_duplicate == 1) {
         error_handler(CANNOT_OVERWRITE_FILE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // c'e' spazio nella directory?
      if(temp_inode.size >= NUM_DIR_ENTRY) {
         error_handler(FULL_DIR_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // se c'e' spazio copio il file e aggiungo la entry
      // chiamo la funzione che alloca il blocco per il nuovo file, e
      // lo scrive nel vfs, mi restituisce l'offset all'i-node da
      // aggiungere alla directory 
      if((allocated_offset_inode = insert_file_vd2vd(path_source, swap,\
                                    source_inode.size)) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // aggiorno l'i-node della directory entry incrementandone il size
      temp_inode.size++;
      
      // preparo la nuova entry per la directory
      strcpy(temp_dir_entry.name,terminal_source);
      temp_dir_entry.i_node = allocated_offset_inode;
      
      // apro il vfs
      if((dsc = open(PATH, O_WRONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione della directory entry
      if(lseek(dsc,temp_inode.block + (temp_inode.size - 1) \
                      * sizeof(DIRECTORY_ENTRY),SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // aggiungo la nuova entry (nome e size)
      if(write(dsc,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                            < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione dell'i-node della directory
      if(lseek(dsc,offset_inode_directory,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(dsc);
         return GEN_FAIL;
      }
      
      // a questo punto posso aggiornare l'inode
      if(write(dsc,&temp_inode,I_NODE_SIZE) \
               < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);       
         close(dsc);
         return GEN_FAIL;
      }
      
      // chiudo il file
      close(dsc);

      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      return EXIT_SUCCESS; 
   }
}


// restituiamo come int l'offset
int insert_file_vd2vd(char *path_dest, char *swap, int size) {
   int dsc, offset_temp_inode;
   I_NODE temp_inode;
   
   // alloco un nuovo blocco libero
   if((offset_temp_inode = allocate_block(RFILE, &temp_inode)) \
                                                         == GEN_FAIL) {
     free(swap);
     return GEN_FAIL;
   }
   
   // scrivo la size del file
   temp_inode.size = size;
   
   // aggiorno l'i-node:
   
   // accedo al file system virtuale
   if((dsc = open(PATH,O_WRONLY)) == GEN_FAIL) {
      free(swap);
      return GEN_FAIL;
   }
   
   // mi sposto all'offset dell'i-node nel vfs
   if(lseek(dsc,offset_temp_inode,SEEK_SET) == GEN_FAIL) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }

   // scrivo l'i-node
   if(write(dsc,&temp_inode,I_NODE_SIZE) < I_NODE_SIZE) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }
   
   // mi sposto nel punto corrispondente al blocco in cui devo scrivere
   // il file
   if(lseek(dsc,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }

   // a questo punto posso scrivere il blocco
   if(write(dsc,swap,size) < size) {
      close(dsc);
      free(swap);
      return GEN_FAIL;
   }
   
   // libero la memoria
   free(swap);
   
   // chiudo il file path_vd
   close(dsc);
   
   return offset_temp_inode;
}

/* fine file */




/* file sorgente create-vfs.c 
 *
 * definizione della funzione che crea il file di nome vfs.tmp in /tmp
 * con tutta la struttura in base a num_files da contenere
 */

#include "base-functions.h"

int create_vfs(int num_files) {
   int i;    // contatore per i for
   int vfs;  // file descriptor
   // aggiungo un I_NODE_SIZE perche' il primo i-node lo
   // preparo gia' per la directory di root
   // fli = spazio_fli + spazio_flb + spazio_inode_root
   int fli = 8 + I_NODE_SIZE;
   // aggiungo un BLOCK_SIZE a flb perche' il primo blocco lo
   // preparo gia' per la directory di root
   // flb = spazio_fli + spazio_flb + spazio_inode + blocchi_occupati
   int flb = 8 + num_files * I_NODE_SIZE + BLOCK_SIZE;
   // variabili di appoggio
   int offset;
   char padding[BLOCK_SIZE-sizeof(int)];
   I_NODE temp;
   // gestiamo il caso in cui num_files sia inferiore a 3
   if(num_files < 3) {
      fprintf(stderr,NUM_FILES_ERROR_MESSAGE);
      return GEN_FAIL;
   }
   
   // creo il file vfs.tmp
   vfs = open(PATH, O_WRONLY | O_CREAT, 0755);
   // se l'apertura del file e' fallita
   if (vfs < 0) {
      perror("vfs file creation failed");
      return GEN_FAIL;
   }
   // inizializzo il valore della testa della free-list degli i-node
   if (write(vfs, &fli, sizeof(int)) < sizeof(int)) {
      perror("vfs fli writing failed");
      close(vfs);
      return GEN_FAIL;
   }
   // inizializzo il valore della testa della free-list dei blocchi lib.
   if (write(vfs, &flb, sizeof(int)) < sizeof(int)) {
      perror("vfs flb writing failed");
      close(vfs);
      return GEN_FAIL;
   }
   // il primo i-node che viene creato e' quello relativo alla cartella
   // radice "/"
   temp.type = DIR;
   temp.timedate = time(NULL);
   temp.sharing = 1;
   temp.block = flb - BLOCK_SIZE;
   // size nel caso di i-node che puntato a directory contiene il numero
   // di file contenuti nella directory stessa
   temp.size = 0;
   // gli i-node utilizzati non sono concatenati
   temp.nextfree = 0;
   if(write(vfs, &temp, I_NODE_SIZE) < I_NODE_SIZE) {
      perror("vfs first i-node writing failed");
      close(vfs);
      return GEN_FAIL;
   }
   
   // i-node liberi
   temp.type = RFILE;
   temp.timedate = 0;
   temp.sharing = 0;
   temp.block = 0;
   temp.size = 0;
   temp.nextfree = 8 + I_NODE_SIZE;
   // creo gli i-node liberi
   for(i = 1; i < num_files - 1; i++){
      // offset che punta al prossimo i-node libero
      temp.nextfree+=I_NODE_SIZE;
      // scrittura dell'i-node
      if(write(vfs,&temp,I_NODE_SIZE) < I_NODE_SIZE) {
         perror("vfs i-node writing failed");
         close(vfs);
         return GEN_FAIL;
      }
   }
   
   // questo siccome e' l'ultimo ha nextfree posto a 0
   temp.nextfree = 0;
   if(write(vfs, &temp, I_NODE_SIZE) < I_NODE_SIZE) {
      perror("vfs last i-node writing failed");
      return GEN_FAIL;
   }
   
   // preparo il padding per i blocchi liberi
   for(i = 0; i < BLOCK_SIZE - sizeof(int); i++)
      padding[i]=0xff;
   
   // creo un primo blocco vuoto (quello associato alla root)
   offset=0;
   if(write(vfs, &offset, sizeof(int)) < sizeof(int)) {
      perror("vfs first block writing failed");
      close(vfs);
      return GEN_FAIL;
   }     
   // scrivo su disco il padding per occupare il blocco           
   if(write(vfs, padding, BLOCK_SIZE - sizeof(int)) < \
      BLOCK_SIZE - sizeof(int)) {
      perror("vfs first block writing failed");
      close(vfs);
      return GEN_FAIL;
   }
   
   // scrivo tutti i blocchi liberi
   offset = flb;
   for(i = 1; i < num_files - 1; i++){
      offset += BLOCK_SIZE;
      if(write(vfs, &offset, sizeof(int)) < sizeof(int)) {
         perror("vfs block writing failed");
         close(vfs);
         return GEN_FAIL;
      }              
      if(write(vfs, padding, BLOCK_SIZE - sizeof(int)) < \
         BLOCK_SIZE - sizeof(int)) {
         perror("vfs block writing failed");
         close(vfs);
         return GEN_FAIL;
      }
   }
   
   // scrivo l'ultimo blocco che non punta a nulla
   offset=0;
   if(write(vfs, &offset, sizeof(int)) < sizeof(int)) {
      perror("vfs last block writing failed");
      close(vfs);
      return GEN_FAIL;
   }           
   if(write(vfs, padding, BLOCK_SIZE - sizeof(int)) < \
      BLOCK_SIZE - sizeof(int)) {
      perror("vfs last block writing failed");
      close(vfs);
      return GEN_FAIL;
   }
   
   // chiudo il file
   if(close(vfs) < 0) {
      perror("closing vfs file failed");
      close(vfs);
      return GEN_FAIL;
   }
   
   close(vfs);
   return EXIT_SUCCESS;
}




/* file sorgente deallocate-block.c
 *
 * definizione della funzione deallocate_block che si occupera' di 
 * scollegare un blocco da un'i-node e renderli entrambi liberi
 */
 
#include "base-functions.h"

int deallocate_block(I_NODE *inode, unsigned int i_node_offset) {
   // variabili di appoggio
   int vfs,fli,flb;
   
   // se il contatore sharing e' maggiore di 1 devo semplicemente 
   // diminuirlo
   if (inode->sharing > 1) {
      inode->sharing -= 1;
      
      // si apre il file in lettura e si gestiscono eventuali errori
      if ((vfs = open(PATH,O_WRONLY)) == -1) {
         return GEN_FAIL;
      }
      
      // mi sposto al punto giusto
      if(lseek(vfs,i_node_offset,SEEK_SET) == GEN_FAIL) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // aggiorno l'inode
      if(write(vfs,inode,I_NODE_SIZE) < I_NODE_SIZE) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // chiudo il file
      close(vfs);
   } else {
      // altrimenti devo rendere liberi sia l'inode sia il blocco
      // collegato.
   
      // si apre il file in lettura e si gestiscono eventuali errori
      if ((vfs = open(PATH,O_RDONLY)) == -1) {
         return GEN_FAIL;
      }
      
      // da qui si leggono fli e flb e si gestiscono gli errori  
      if (read(vfs,&fli,sizeof(int)) < sizeof(int)) {
         close(vfs);
         return GEN_FAIL;
      }
      if (read(vfs,&flb,sizeof(int)) < sizeof(int)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // chiudo il file
      close(vfs);
      
      // inserisco l'i-node in testa
      inode->nextfree = fli;
      
      // si apre il file in scrittura e si gestiscono gli errori
      if (open(PATH,O_WRONLY) == -1)
         return GEN_FAIL;
      
      // aggiorno l'offset in fli
      if (write(vfs,(int *)&i_node_offset,sizeof(int)) < sizeof(int)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // aggiorno l'offset in flb
      if (write(vfs,(int *)&(inode->block),sizeof(int)) < sizeof(int)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // mi sposto sul blocco da modificare
      if (lseek(vfs,inode->block,SEEK_SET) == -1) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // concludo l'inserimento in testa di flb
      if(write(vfs,(int *)&flb,sizeof(int)) < sizeof(int)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      inode->block = 0;
      inode->sharing = 0;
      inode->timedate = 0;
      inode->type = RFILE;
      inode->size = 0;
      
      // mi sposto sull' inode da modificare
      if (lseek(vfs,i_node_offset,SEEK_SET) == -1) {
         close(vfs);
         return GEN_FAIL;
      }
      
      // concludo l'inserimento in testa di fli
      if(write(vfs,inode,sizeof(I_NODE)) < sizeof(I_NODE)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      close(vfs);
   }
   return EXIT_SUCCESS;
}

/* fine file */




/* file sorgente del.c
 *
 * definizione della funzione del che si occupera di 
 * rimuovere con possibilita' di recupero un
 * file o un link hard
 */

#include <string.h> 
#include "base-functions.h"

int del(char *path, char **message) {
   // inode temporaneo
   I_NODE temp_inode;
   // offset dell'inode
   int temp_inode_offset;
   // parte sinistra del path
   char *safe_path;
   // parte destra del path
   char *terminal_name;
   // DIRECTORY_ENTRY di appoggio
   DIRECTORY_ENTRY temp_dir_entry;
   // descrittore per il file system virtuale
   int vfs;
   // contatore
   int i;
   
   // se il path non contiene barre (e' malformato) usciamo e
   // mi ricavo la parte destra del path
   if((terminal_name = strrchr(path,'/')) == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // elimino la barra
   terminal_name++;
   
   // controllo se il path esiste
   if((temp_inode_offset = get_i_node(path, &temp_inode)) == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se si tratta di un file gia' cancellato
   if(temp_inode_offset<0) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // controllo che non stiamo tentando di cancellare una directory
   if((temp_inode.type == DIR) && (temp_inode.size != 0)){
      error_handler(CANNOT_DELETE_DIR_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // mi ricavo la parte sinistra del path
   safe_path = (char*)malloc((terminal_name - path)+1);
   safe_path[0] = '\0';
   strncat(safe_path, path,(size_t)((terminal_name - path)));
   
   // mi ricavo l'inode della cartella in cui e' contenuto il file da
   // eliminare (dobbiamo modificare la rispettiva directory entry)
   if((temp_inode_offset = get_i_node(safe_path, &temp_inode)) \
                                                         == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // apriamo il file del vfs
   if((vfs = open(PATH,O_RDWR)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // ci spostiamo all'offset della directory table
   if(lseek(vfs,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   
   // ciclo per trovare l'entry giusta, quando la troviamo usciamo
   // dal ciclo
   i=0;
   while(strcmp(temp_dir_entry.name, terminal_name) != 0){
 
      // leggiamo una entry
      if(read(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      i++;
   }
      
   if(lseek(vfs,((i-1)*sizeof(DIRECTORY_ENTRY))+ temp_inode.block, \
                                             SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   
   // ricordiamo che l'entry giusta deve essere trovata per forza, a
   // causa del controllo all'inizio del listato
   // la modifichiamo rendendo negativo il valore del campo inode
   temp_dir_entry.i_node = - temp_dir_entry.i_node;
   // e la riscriviamo
   if(write(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                            < sizeof(DIRECTORY_ENTRY)) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // a questo punto aggiorno il campo size dell'inode della
   // directory padre
   //temp_inode.size--;
   // e vado a scrivere la modifica
   if(lseek(vfs,temp_inode_offset,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   if(write(vfs,&temp_inode,I_NODE_SIZE) < I_NODE_SIZE) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // chiudo il file
   close(vfs);
   
   // termino con successo
   (*message)[0] = '0';
   (*message)[1] = '\0';
   
   return EXIT_SUCCESS;
}

/* fine file */




/* file sorgente error_handler.c
 *
 * funzione che gestisce la creazione dei messaggi di errore in memoria
 * dinamica (sono poi passati al server che gestisce la comunicazione)
 */

#include <string.h>
#include <stdlib.h>

void error_handler(char *error, char **message){
   // copio un 1 come primo carattere di *message (errore)
   (*message)[0] = '1';
   (*message)[1] = '\0';
   // ridimensiono l'array in mem. din. per contenere l'output
   *message = (char *)realloc(*message, 1 + strlen(error) + 1);
   // concateno in *message il messaggio di errore specifico
   strcat(*message, error);
}

/* fine file */




/* file sorgente get-i-node.c
 *
 * definizione della funzione get_i_node che restituisce 
 * l'offset all'i-node associato ad uno specifico path
 */
 
#include <string.h>
#include "base-functions.h"

int free_exploded_path(char * exploded_path[], int j);

int get_i_node(char * path, I_NODE * inode) {
   // path suddiviso in token
   char * exploded_path[100];
   char temp;
   int i,k;
   // conterra' il numero di token
   int j=0;
   int num_el;
   // inizializzato alla posizione dell'i-node di /
   int seek_temp = ROOT_DIR_OFFSET;
   // file descriptor
   int vfs;
   // puntatore ad i-node per quando lo leggo da vfs
   I_NODE *inode_in = (I_NODE *)malloc(sizeof(I_NODE));
   // array per il blocco della directory
   DIRECTORY block_in;
   // boolean per ricordare se ho fatto seek su un file
   // cancellato in modo soft
   int deleted = 0;
   
   // il path deve iniziare con una /
   if(path[0] != '/') {
      free(inode_in);
      return GEN_FAIL;
   }
   
   // se si vuole accedere alla root e' banale
   if(path[1] == '\0') {
      // apro il file vfs
      vfs = open(PATH,O_RDONLY);
      if(vfs == -1) {
         free(inode_in);
         return GEN_FAIL;
      }
      // mi sposto opportunamente all'i-node
      if(lseek(vfs,seek_temp,SEEK_SET) == -1) {
         close(vfs);
         free(inode_in);
         return GEN_FAIL;
      }
         
      // lettura dell'i-node
      if(read(vfs,inode_in,I_NODE_SIZE) < I_NODE_SIZE) {
         close(vfs);
         free(inode_in);
         return GEN_FAIL;
      }
   } else {
      
      // inizializzo le variabili necessarie
      i = j = k = num_el = 0;
      temp = '0';
      
      // leggo tutto il path in ingresso
      while(temp != '\0') {
         i++;
         // carico ogni carattere
         temp = path[i];
         // controllo se sono arrivato alla fine di un token
         if(temp == '/' || temp == '\0') {
            if(num_el != 0) {
               // se sono alla fine del token inserisco il \0
               exploded_path[j][num_el] = '\0';
               // e mi preparo per il prossimo token
               j++;
               num_el = 0;
            }
         } else {
            // se leggo una lettera del token
            if(num_el == 0) {
               // se e' la prima alloco memoria
               exploded_path[j] = (char *)malloc(sizeof(char));
            }
            // la scrivo
            exploded_path[j][num_el] = temp;
            num_el++;
            // incremento la dimensione in memoria
            exploded_path[j] = (char *)realloc(exploded_path[j], \
                                          (1 + num_el) * sizeof(char));
         }
      }
   
      // apro il file vfs
      vfs = open(PATH,O_RDONLY);
      if(vfs == -1) {
         free(inode_in);
         free_exploded_path(exploded_path, j);
         return GEN_FAIL;
      }
         
      for(i = 0; i <= j; i++) {
         if(seek_temp<0) {
            seek_temp = - seek_temp;
            deleted=1;
         }
         // mi sposto opportunamente all'i-node
         if(lseek(vfs,seek_temp,SEEK_SET) == -1) {
            close(vfs);
            free(inode_in);
            free_exploded_path(exploded_path, j);
            return GEN_FAIL;
         }
         
         if(deleted==1) {
            seek_temp = - seek_temp;
            deleted = 0;
         }
         
         // lettura dell'i-node
         if(read(vfs,inode_in,I_NODE_SIZE) < I_NODE_SIZE) {
            close(vfs);
            free(inode_in);
            free_exploded_path(exploded_path, j);
            return GEN_FAIL;
         }
         
         // controllo se non e' una directory
         if(inode_in->type != DIR) {
            // se non lo e' ma non siamo alla fine: errore
            if(i != j) {
               close(vfs);
               free(inode_in);
               free_exploded_path(exploded_path, j);
               return GEN_FAIL;
            }
            // esco per restituire l'i-node
            break;
         } else {
            // se si tratta dell'ultima directory
            if(i == j) {
               // esco per restituire l'i-node
               break;
            }
         }
         
         // mi sposto alla posizione del blocco puntato dall'i-node
         if(lseek(vfs,inode_in->block,SEEK_SET) == -1) {
            close(vfs);
            free(inode_in);
            free_exploded_path(exploded_path, j);
            return GEN_FAIL;
         }
         
         // lettura dal blocco delle strutture di directory
         for(k = 0; k < NUM_DIR_ENTRY; k++) {
            if(read(vfs,&block_in[k],sizeof(DIRECTORY_ENTRY)) \
               < sizeof(DIRECTORY_ENTRY)) {
               close(vfs);
               free(inode_in);
               free_exploded_path(exploded_path, j);
               return GEN_FAIL;
            }
         }
         
                  
         // cerchiamo all'interno della directory il prox token
         for(k = 0; k < NUM_DIR_ENTRY; k++) {
            if(strcmp(exploded_path[i],block_in[k].name)==0) {
               // abbiamo trovato il token e aggiorniamo
               seek_temp = block_in[k].i_node;
               // esco dal ciclo interno
               break;
            }
         }
                  
         // se siamo arrivati alla fine senza trovare il token
         if(k == NUM_DIR_ENTRY) {
            // il path non e' valido!
            close(vfs);
            free(inode_in);
            free_exploded_path(exploded_path, j);
            return GEN_FAIL;
         }
      }
      free_exploded_path(exploded_path,j);
   }


   // chiudo il file
   close(vfs);
   
   // aggiorno il parametro
   inode->type=inode_in->type;
   inode->timedate=inode_in->timedate;
   inode->sharing=inode_in->sharing;
   inode->block=inode_in->block;
   inode->size=inode_in->size;
   inode->nextfree=0;   // e' banale
   
   free(inode_in);
   
   
   // return dell'offset all'i-node
   return seek_temp;
}

int free_exploded_path(char * exploded_path[], int j) {
   int i;
  for(i = 0; i < j; i++)
      free(exploded_path[i]);
   return 0;
}

/* fine file */




/* file sorgente ln.c
 *
 * definizione delle funzioni che si occupano di effettuare la
 * creazione di link simbolici e link hard all'interno del VFS
 */

#include <sys/stat.h> 
#include <string.h>
#include "base-functions.h"

int ln(char * target, char * link_name, char ** message) {
   // descrittore per gli accessi al disco virtuale
   int vfs;
   // parte dx del path "target" (dopo l'ultima '/')
   char * terminal_target;
   // parte dx del path "link_name" (dopo l'ultima '/')
   char * terminal_link;
   // parte sx del path "link_name" (prima dell'ultima '/')
   char *safe_link;
   // variabile per la struttura i-node del target
   I_NODE target_inode;
   // variabile per la struttura i-node del link_name
   I_NODE link_inode;
   // offset dell'i-node della directory che conterra' il link
   int link_inode_offset;
   // offset dell'i-node del file target
   int target_inode_offset;
   // directory entry temporanea
   DIRECTORY_ENTRY temp_dir_entry;
   // flag per i duplicati
   int found_duplicate = 0;
   // indice per i cicli
   int k;
   
   // controllo che il target sia ben formato
   if((terminal_target = strrchr(target,'/')) == NULL) {
      error_handler(INVALID_TARGET_PATH_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // controllo che il target esista e prendo l'offset del suo i-node
   // di cui dovro' aggiornare il campo sharing
   if((target_inode_offset = get_i_node(target, &target_inode)) \
                             == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // controllo che il target non sia una directory
   if(target_inode.type == DIR) {
      error_handler(CANNOT_LINK_DIR_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // controllo che il link_name sia ben formato
   if((terminal_link = strrchr(link_name,'/')) == NULL) {
      error_handler(INVALID_LINK_PATH_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // elimino la barra iniziale da terminal_link
   terminal_link++;
   
   // ottengo la parte sx del link_name
   safe_link = (char*)malloc((terminal_link - link_name)+1);
   safe_link[0] = '\0';
   strncat(safe_link, link_name,(size_t) ((terminal_link - link_name)));
   
   // controllo che il safe_link sia valido
   if((link_inode_offset = get_i_node(safe_link,&link_inode))\
                           == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      free(safe_link);
      return GEN_FAIL;
   }
   
   free(safe_link);
   
   // rileva un altro tipo di path (linkname) malformato, ossia rileva
   // quando stiamo tentando di creare un link hard all'interno di un
   // file (trattandolo come se fosse una directory)
   if(link_inode.type == RFILE) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se terminal_link e' vuoto inserisco terminal_target
   // (siamo nel caso in cui creare un link con il nome originale)
   terminal_target = strrchr(target,'/')+1;
   if(strcmp(terminal_link,"") == 0) {
      strncat(terminal_link,terminal_target,256);
   }
   
   // la directory contiene gia' una entry terminal_link?
   if((vfs = open(PATH,O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // mi sposto alla posizione del blocco della directory table
   // non gestisco l'errore perche' non puo' fallire
   if(lseek(vfs,link_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // controllo se esiste gia' una entry con lo stesso nome nella 
   // directory
   found_duplicate = 0;
   for(k = 0; k < NUM_DIR_ENTRY; k++) {
      if(read(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
         < sizeof(DIRECTORY_ENTRY)) {
         close(vfs);
         return GEN_FAIL;
      }
      
      if(strcmp(temp_dir_entry.name,terminal_link) == 0) {
         found_duplicate = 1;
         break;
      }
   }
   
   // chiudo il file
   close(vfs);
   
   // se non c'e' la entry
   if(found_duplicate == 0) {
      // controllo se la directory e' piena (limite max entry)
      if(link_inode.size >= NUM_DIR_ENTRY) {
         error_handler(FULL_DIR_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // in questo ramo devo creare un link con il nome originale
      // controllo che non esistano file con lo stesso nome del file 
      // originale
      
      // incremento il size dell'inode della directory entry
      link_inode.size++;
 
      // preparo la nuova entry per la directory
      strcpy(temp_dir_entry.name,terminal_link);
      temp_dir_entry.i_node = target_inode_offset;
      
      // apro il vfs
      if((vfs = open(PATH, O_WRONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione della directory entry
      if(lseek(vfs,link_inode.block + \
                (link_inode.size - 1) \
                  * sizeof(DIRECTORY_ENTRY),SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // aggiungo la nuova entry (nome e size)
      if(write(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
               < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione dell'i-node della directory
      if(lseek(vfs,link_inode_offset,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // a questo punto posso aggiornare l'inode
      if(write(vfs,&link_inode,I_NODE_SIZE) \
               < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);       
         close(vfs);
         return GEN_FAIL;
      }
      
      // ora devo scrivere l'inode del file originale (ho modificato
      // lo sharing)
      target_inode.sharing++;
      
      // e ora lo aggiorno su disco:
      // mi sposto alla posizione dell'i-node del target
      if(lseek(vfs,target_inode_offset,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // e lo scrivo
      if(write(vfs,&target_inode,I_NODE_SIZE) \
               < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);       
         close(vfs);
         return GEN_FAIL;
      }
      
      // chiudo il file
      close(vfs);
      
      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      
      return EXIT_SUCCESS;  
      
   }else{
   // se invece ho un duplicato
      // ottengo il suo i-node
      if((link_inode_offset = get_i_node(link_name, &link_inode)) \
                              == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }

      // se non e' una directory errore
      if(link_inode.type != DIR) {
         error_handler(CANNOT_OVERWRITE_FILE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // in questo ramo controllo se la directory contiene gia' una 
      // entry terminal_target, se si' errore
      
      if((vfs = open(PATH,O_RDONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
   
      // mi sposto alla posizione del blocco della directory table
      if(lseek(vfs,link_inode.block,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
   
      // controllo se esiste gia' una entry nella directory
      found_duplicate = 0;
      for(k = 0; k < NUM_DIR_ENTRY; k++) {
         if(read(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
            close(vfs);
            return GEN_FAIL;
         }
      
         if(strcmp(temp_dir_entry.name,terminal_target) == 0) {
            found_duplicate = 1;
            break;
         }
      }
      
      // chiudo il file
      close(vfs);
      
      // controllo che la directory non sia piena
      if(link_inode.size >= NUM_DIR_ENTRY) {
         error_handler(FULL_DIR_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // aggiorno l'i-node della directory entry incrementandone size
      link_inode.size++;
      
      // preparo la nuova entry per la directory
      strcpy(temp_dir_entry.name,terminal_target);
      temp_dir_entry.i_node = target_inode_offset;
      
      // apro il vfs
      if((vfs = open(PATH, O_WRONLY)) == GEN_FAIL) {
         error_handler(OPEN_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione della directory entry
      if(lseek(vfs,link_inode.block + \
                (link_inode.size - 1) \
                  * sizeof(DIRECTORY_ENTRY),SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // aggiungo la nuova entry (nome e size)
      if(write(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
               < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // mi sposto alla posizione dell'i-node della directory
      if(lseek(vfs,link_inode_offset,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // a questo punto posso scrivere l'inode
      if(write(vfs,&link_inode,I_NODE_SIZE) \
               < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // ora devo aggiornare l'inode del file originale (ho modificato
      // lo sharing)
      target_inode.sharing++;
      
      // mi sposto alla posizione dell'i-node del target
      if(lseek(vfs,target_inode_offset,SEEK_SET) == GEN_FAIL) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
      
      // a questo punto posso scrivere l'inode
      if(write(vfs,&target_inode,I_NODE_SIZE) \
               < I_NODE_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);       
         close(vfs);
         return GEN_FAIL;
      }
      
      // chiudo il file
      close(vfs);
      
      // termino con successo
      (*message)[0] = '0';
      (*message)[1] = '\0';
      
      return EXIT_SUCCESS; 
   }
}

/* fine file */




/* file sorgente ls.c
 *
 * definizione della funzione ls che si occupera' di listare
 * il contenuto della directory puntata da un certo i-node con
 * varie informazioni aggiuntive
 */

#include <string.h>
#include "base-functions.h"

int ls(char *path, char ** message){
   // variabili temporanee per memorizzare gli inode letti
   I_NODE inode, sub_inode;
   // descrittore per accedere al file system
   int vfs;
   // variabili di appoggio e contatori
   int i, num_el, offset, block_offset;
   char temp[FILE_NAME_SIZE];
   char * aux;
   char * time_aux;
   char * path_temp;
   char buffer[30];
   char timedate[26];
   int num_files=0, num_folders=0;
   int offset_inode;
   // variabile per la conversione da timestamp a stringa 'umana'
   time_t time_t_aux;

   // restituisco il puntatore all'ultima '/' in path
   aux = strrchr(path,'/');

   // gestione path malformato
   if(aux == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }

   // inizializzo il descrittore per accedere ai dati, in sola lettura
   if ((vfs = open(PATH, O_RDONLY)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // ricaviamo l'i-node corrispondente alla directory da listare
   if ((offset = get_i_node(path, &inode)) == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }

   // se il campo size dell'inode e' nullo significa che la cartella
   // ad esso relativa non contiene niente, quindi non ci serve
   // e restituisco successo dando il messaggio di directory vuota
   if (inode.size == 0) {
      // copio uno 0 come primo carattere di message (esito positivo)
      (*message)[0] = '0';
      (*message)[1] = '\0';

      // ridimensiono l'array in mem. din. per contenere l'output
      *message = (char *)realloc(*message, \
                                     1 + strlen(VOID_DIR_MESSAGE) + 1);
      // concateno in message il messaggio che la cartella e' vuota
      strcat(*message, VOID_DIR_MESSAGE);
      return EXIT_SUCCESS;
   }

   // per chiarezza assegno l'indirizzo del blocco che ci interessa ad
   // una variabile
   block_offset = inode.block;

   // copio uno 0 come primo carattere di message (esito positivo)
   (*message)[0] = '0';
   (*message)[1] = '\0';

   // ora posso leggere il contenuto della colonna name della tabella 
   // DIRECTORY_ENTRY
   // ciclo n volte, con n = size dell'inode, ossia al numero di entry
   for (i = 0; i < inode.size; i++){

      if(i==0) {
         // ridimensiono l'array in mem. din. per contenere l'output
         *message = (char *)realloc(*message, 30);
         // aggiungo la maschera a message
         sprintf(buffer, "Date\t\t\t\tSharing\t\tSize\t\tName\n");
         strncat(*message, buffer, 30);
      }

      // per chiarezza teniamo un indice delle voci inserite
      num_el = i+1;

      if(lseek(vfs,block_offset + i * sizeof(DIRECTORY_ENTRY), \
                                                     SEEK_SET) == -1) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }

      // leggo il campo name della struttura directory
      if(read(vfs,temp,FILE_NAME_SIZE) < FILE_NAME_SIZE) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }

      // ridimensiono adeguatamente l'array di output, per contenere:
      // 0/1, tabulazione, barra in caso di directory, tabulazione,
      // dimensione directory/file, tabulazione, nome della entry,
      // tabulazione, orario, \n e \0
      // il tutto ripetuto per il numero di entry e sommato alla
      // dimensione della stringa con il numero di file e cartelle
      *message = (char *)realloc(*message,\
           (strlen(temp)+52)*num_el+27+30);

      // ricavo l'inode dell'entry che mi interessa e concateno in
      // message le varie informazioni

      // creo il path per la get_i_node
      path_temp = (char *)malloc(sizeof(char) * \
                                (strlen(path) + 1 + strlen(temp) + 1));
      strcpy(path_temp,path);
      strncat(path_temp,"/",1);

      // leggo l'i-node
      offset_inode = get_i_node(strncat(path_temp,temp, \
                                         FILE_NAME_SIZE), &sub_inode);

      // libero la memoria
      free(path_temp);

      if(offset_inode>0) {
         // concateno in message la stringa con data/orario
         time_t_aux = (time_t)sub_inode.timedate;
         time_aux = ctime(&time_t_aux);
         strcpy(timedate, time_aux);
         timedate[24]='\0';
         strncat(*message, timedate, 24);
   
         // concateno in message una tabulazione
         strncat(*message, "\t",1);
   
         // concateno lo sharing
         sprintf(buffer, "%d", sub_inode.sharing);
         strncat(*message, buffer, 30);
   
         // concateno in message una tabulazione
         strncat(*message, "\t\t",2);
   
         // concateno la dimensione
         sprintf(buffer, "%d", sub_inode.size);
         strncat(*message, buffer, 30);
   
         // concateno in message una tabulazione
         strncat(*message, "\t\t", 2);
   
         // controllo se si tratta di una directory e nel caso aggiungo
         // una '/' all'inizio del nome
         if(sub_inode.type == DIR) {
            strncat(*message,"/",1);
            strncat(*message, temp, FILE_NAME_SIZE);
            // incremento il contatore delle directory
            num_folders++;
         } else {
            // se e' un file concateno semplicemente il nome
            strncat(*message, temp, FILE_NAME_SIZE);
            // incremento il contatore dei file
            num_files++;
         }
   
         // concateno un ritorno a capo
         strncat(*message, "\n",1);
     }
   }

   // concateno il messaggio con il numero di file e cartelle
   sprintf(buffer, "%d files and %d folders", num_files, num_folders);
   strncat(*message, buffer, 30);

   // chiudo il descrittore
   close(vfs);

   // restituisco un exit status positivo
   return EXIT_SUCCESS;
}




/* file sorgente mkdir.c
 *
 * definizione della funzione mkdir che crea una directory 
 * all'interno di un path
 */

#include <string.h>
#include "base-functions.h"

int mkdir(char * path, char ** message) {
   // variabili di appoggio
   char * aux;
   char * temp;
   I_NODE found;
   I_NODE new;
   // offset alla posizione degli i-node
   int offset_new;
   int offset_inode;
   // contatori per i cicli
   int i,k;
   // descrittore per il vfs
   int vfs;
   // array per il blocco della directory
   DIRECTORY block_in;

   // restituisco il puntatore all'ultima '/' in path
   aux = strrchr(path,'/');

   // gestione path malformato
   if(aux == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }

   // se l'ultimo carattere e' una '/' la sostituisco con uno '\0'
   if((aux - path) == strlen(path) - 1) {
      path[(aux - path)] = '\0';
      aux = strrchr(path,'/');
   }

   // gestione nel caso in cui il path sia gia' esistente
   if(get_i_node(path,&found) != GEN_FAIL) {
      error_handler(PATH_ALREADY_EXIST_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // se il puntatore all'ultima '/' coincide con l'inizio allora
   // devo accedere direttamente alla root
   if(aux == path) { 
      // ottengo l'i-node di '/'
      offset_inode = get_i_node("/",&found);
      if(offset_inode == GEN_FAIL) {
         error_handler(GET_INODE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
   } else {
      // se invece devo accedere ad un certo path creo la stringa da 
      // passare a get_i_node che e' formata da tutto il path esclusa 
      // la nuova cartella da creare
      // abbiamo usato l'aritmetica dei puntatori per avere la
      // lunghezza della stringa da allocare (aux-path)
      temp = calloc((aux-path) + 1, sizeof(char));
      for(i = 0; i < (aux-path); i++) {
         temp[i] = path[i];
      }
      temp[i] = '\0'; 

      // ottengo l'inode di tale path
      offset_inode = get_i_node(temp, &found);
      if(offset_inode == GEN_FAIL) {
         free(temp);
         error_handler(GET_INODE_ERROR_MESSAGE, message);
         return GEN_FAIL;
      }
   }

   // se il tipo e' file dai errore: non si possono creare cartelle
   // al di sotto dei file
   if(found.type == RFILE) {
      error_handler(NOT_A_DIRECTORY_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // errore se non c'e' piu' spazio nella directory
   if(found.size == NUM_DIR_ENTRY) {
      error_handler(FULL_DIR_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // apro il file vfs per leggere la struttura della directory
   // in modo da poterne poi aggiornare i valori
   vfs = open(PATH,O_RDONLY);
   if(vfs == -1) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // mi sposto alla posizione del blocco puntato dall'i-node
   if(lseek(vfs,found.block,SEEK_SET) == -1) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // lettura dal blocco delle strutture di directory
   for(k = 0; k < NUM_DIR_ENTRY; k++) {
      if(read(vfs,&block_in[k],sizeof(DIRECTORY_ENTRY)) \
         < sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
         close(vfs);
         return GEN_FAIL;
      }
   }

   // chiudo il file
   close(vfs);

   // alloco un blocco per la nuova directory
   if((offset_new = allocate_block(DIR,&new)) == GEN_FAIL) {
      error_handler(FULL_DISK_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // aggiorno il blocco della directory madre
   found.size++;  // incremento il numero di elementi
   // scrivo nella struttura del blocco la nuova entry
   strcpy(block_in[found.size].name,(aux+1));
   block_in[found.size].i_node = offset_new;

   // aggiorno sul vfs l'i-node e il blocco modificati
   // apro il file
   vfs = open(PATH,O_WRONLY);
   if(vfs == -1) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }

   // mi sposto alla posizione dell'i-node
   if(lseek(vfs,offset_inode,SEEK_SET) == -1) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   // scrivo l'inode aggiornato
   if(write(vfs,&found,I_NODE_SIZE) < I_NODE_SIZE) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   // mi sposto alla posizione del blocco
   if(lseek(vfs, found.block + \
                (found.size - 1) * sizeof(DIRECTORY_ENTRY) \
                ,SEEK_SET) == -1) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   // scrivo l'entry aggiornata nel blocco
   if(write(vfs,&(block_in[found.size]),sizeof(DIRECTORY_ENTRY)) \
               < sizeof(DIRECTORY_ENTRY)) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   // chiudo il file
   close(vfs);

   // e' andato tutto bene
   (*message)[0] = '0';
   (*message)[1] = '\0';
   return EXIT_SUCCESS;
}
/* fine file */




/* file sorgente purge.c
 *
 * definizione della funzione purge che si occupera di 
 * rimuovere completamente senza possibilita' di recupero un
 * file o un link hard
 *
 */

#include <string.h> 
#include "base-functions.h"

int purge(char *path, char **message) {
   // inode che si riferisce al path
   I_NODE temp_inode;
   // offset dell'inode
   int offset_temp_inode;
   // parte sinistra del path
   char *left_side;
   // parte destra del path
   char *terminal_name;
   // array che rappresenta una directory
   DIRECTORY dir;
   // descrittore del file
   int vfs;
   // variabili d'appoggio
   char *aux;
   int i,k;
   
   // controllo che il path sia ben formato
   aux = strrchr(path,'/');
   if(aux == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se l'ultimo carattere e' una '/' la sostituisco con uno '\0'
   if((aux - path) == strlen(path) - 1) {
      path[(aux - path)] = '\0';
      aux = strrchr(path,'/');
   }
   
   // acquisisco l'inode da eliminare
   if((offset_temp_inode = get_i_node(path,&temp_inode)) == GEN_FAIL) {
      error_handler(GET_INODE_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se l'inode e' negativo (c'e' stata una cancellazione soft)
   // lo inverto di segno per renderlo positivo e utilizzarlo
   if(offset_temp_inode < 0) {
      offset_temp_inode = -offset_temp_inode;
   }
   
   // se l'inode rappresenta una directory con almeno un elemento
   // restituisco immediatamente un errore
   if((temp_inode.type == DIR) && (temp_inode.size > 0)) {
      error_handler(CANNOT_PURGE_NOT_EMPTY_DIR_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   if(deallocate_block(&temp_inode,offset_temp_inode) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      return GEN_FAIL;
   }
   
   // se il path non contiene barre (e' malformato) usciamo
   if((terminal_name = strrchr(path,'/')) == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // se la strrchr va a buon fine con il ++ eliminiamo la barra 
   // iniziale che non ci interessa
   terminal_name++;
   
   // inserisco in left_side la parte sinistra
   left_side = (char*)malloc((terminal_name - path)+1);
   left_side[0] = '\0';
   strncat(left_side, path,(size_t)((terminal_name - path)));
   
   if((offset_temp_inode = get_i_node(left_side,&temp_inode)) == \
                                                             GEN_FAIL) {
      error_handler(GET_INODE_ERROR_MESSAGE, message);
      free(left_side);
      return GEN_FAIL;
   }

   // se l'inode e' negativo (c'e' stata una cancellazione soft)
   // lo inverto di segno per renderlo positivo e utilizzarlo
   if(offset_temp_inode < 0)
      offset_temp_inode = -offset_temp_inode;

   // si apre il file in lettura e si gestiscono eventuali errori
   if ((vfs = open(PATH,O_RDONLY)) == -1) {
      return GEN_FAIL;
   }
   
   // mi posiziono al punto giusto
   if(lseek(vfs,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      free(left_side);
      close(vfs);
      return GEN_FAIL;
   }

   // carico in memoria le directory entry
   for(i = 0; i < temp_inode.size; i++) {
      // leggo una directory entry  
      if(read(vfs,&(dir[i]),sizeof(DIRECTORY_ENTRY)) <  \
                                              sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
         free(left_side);
         close(vfs);
         return GEN_FAIL;
      }
   }
   
   // chiudo il file
   close(vfs);
   
   // disalloco la memoria
   free(left_side);
   
   // scansiono tutte le dir entry finche' non trovo quella giusta
   for(i = 0; i < temp_inode.size; i++) {
      if(strcmp(dir[i].name,terminal_name) == 0) {
         break;
      }
   }
   
   // questo controllo non sarebbe strettamente necessario:
   // se la get_i_node non aveva dato errori, vuol dire che 
   // la entry era giĆ  stata trovata
   if(i == temp_inode.size) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      return GEN_FAIL;
   }
   
   // tiro su tutte le dir entry seguenti a quella cancellata
   for(k = i + 1; k < temp_inode.size; k++, i++) {
      dir[i] = dir[k];
   }
   
   // cancello l'ultima entry che e' stata inevitabilmente tirata su
   strcpy((dir[k - 1]).name, "");
   (dir[k - 1]).i_node = -1;
   
   // aggiorno le directory
   // si apre il file in lettura e si gestiscono eventuali errori
   if ((vfs = open(PATH,O_WRONLY)) == -1) {
      return GEN_FAIL;
   }
   
   // mi posiziono al punto giusto
   if(lseek(vfs,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      close(vfs);
      return GEN_FAIL;
   }

   // scrivo su disco le directory entry
   for(i = 0; i < temp_inode.size; i++) {
      // scrivo una directory entry  
      if(write(vfs,&(dir[i]),sizeof(DIRECTORY_ENTRY)) <  \
                                              sizeof(DIRECTORY_ENTRY)) {
         error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
         close(vfs);
         return GEN_FAIL;
      }
   }
   
   // evito che size vada a valori negativi
   if(temp_inode.size>0) {
      temp_inode.size--;
   }
   
   // vado ad aggiornare la nuova size dell'inode:
   // mi posiziono nel punto giusto
   if(lseek(vfs,offset_temp_inode,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      close(vfs);
      return GEN_FAIL;
   }
  
   // scrivo l'i-node
   if(write(vfs,&temp_inode,I_NODE_SIZE) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE,message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // chiudo il file
   close(vfs);
   
   // termino con successo
   (*message)[0] = '0';
   (*message)[1] = '\0';
   
   return EXIT_SUCCESS;
}

/* fine file */




/* file sorgente server.c
 *
 * sorgente del server
 */
 
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <signal.h>
#include "base-functions.h"

int make_named_socket(const char *filename);
void suicide(int sig);

// funzioni ad 'alto' livello
int ls(char *path, char ** message);
int mkdir(char * path, char ** message);
int cp_fs2vd(char * path_fs, char * path_vd, char **message);
int cp_vd2fs(char * path_vd, char * path_fs, char **message);
int cp_vd2vd(char *path_source, char *path_dest, char **message);
int ln(char * target, char * link_name, char ** message);
int purge(char *path, char **message);
int del(char *path, char **message);
int undel(char *path, char **message);

int main(int argc, char *argv[]) {
   int sock;
   int client;
   char message='0';
   char comando[100][100];
   int i=0,j;
   const char *filename = SOCK_PATH;
   // verra' riempita con l'indirizzo del client
   struct sockaddr addr;
   // verra' riempita con dimensione della struttura addr
   unsigned int adLen = sizeof(struct sockaddr_un);
   char * output;

   // cancella e crea il file temporaneo create-vfs
   unlink(PATH);
   if(create_vfs(VFS_NUM_FILES)==-1) {
      fprintf(stderr,"Error creating vfs temporary file\n");
      unlink(PATH);
      return -1;
   }
   
   // intercetto i segnali di terminazione (se possibile)
   // per poter cancellare il file temporaneo
   
   signal(SIGHUP,suicide);
   signal(SIGINT,suicide);
   signal(SIGPIPE,suicide);
   signal(SIGALRM,suicide);
   signal(SIGTERM,suicide);
   signal(SIGUSR1,suicide);
   signal(SIGUSR2,suicide);
   signal(SIGSTOP,suicide);
   signal(SIGTTIN,suicide);
   signal(SIGTTOU,suicide);
   signal(SIGPROF,suicide); 

   // elimina un eventuale socket e lo crea
   unlink(filename);
   sock = make_named_socket(filename);   
   
   // dichiara il num di richieste accodabili con listen()
   if(listen(sock,1) != 0) {
      perror("listen");
      exit(EXIT_FAILURE);
   }
   
   // divento demone
   daemon(0,0);
   
   while(1) {
      // accetta una connessione con accept()
      if ((client = accept(sock, &addr, &adLen)) == -1) {
         perror("accept");
         exit(EXIT_FAILURE);
      }

      // opera sul socket con read() e write()
      message = '0';
      i = 0;
      while(message != '&') {
         message = '0';
         j = 0;
         while(message != '\0' && message != '&') {
            read(client,&message,1);
            // non devo inserire l'& di terminazione
            if(message != '&') {
               comando[i][j] = message;
               j++;
            }
         }
         i++; // variabile importante indica il numero di parole
      }
      
      // inizializzo output in memoria dinamica
      output = (char *)malloc(sizeof(char)*2);
      
      // eseguo il comando
      if(strcmp(comando[0],"cp_fs2vd") == 0 && i == 4) {
         cp_fs2vd(comando[1],comando[2],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"cp_vd2fs") == 0 && i == 4) {
         cp_vd2fs(comando[1],comando[2],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"cp_vd2vd") == 0 && i == 4) {
         cp_vd2vd(comando[1],comando[2],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"ln") == 0 && i == 4) {
         ln(comando[1],comando[2],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"ls") == 0 && i == 3) {
         ls(comando[1],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"mkdir") == 0 && i == 3) {
         mkdir(comando[1],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"del") == 0 && i == 3) {
         del(comando[1],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"undel") == 0 && i == 3) {
         undel(comando[1],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"purge") == 0 && i == 3) {
         purge(comando[1],&output);
         write(client,output,strlen(output) + 1);
      } else if(strcmp(comando[0],"stop") == 0 && i == 2) {
         output[0]='0';
         output[1]='\0';
         write(client,output,strlen(output) + 1);
         unlink(PATH);
         free(output);
         close(client);
         close(sock);
         return 0;
      } else {
         write(client,"1Command not found",19);
      }
      
      free(output);
      
      // chiudo i socket
      close(client);
   }
   
   close(sock);
	
   return 0;
}

int make_named_socket(const char *filename) {
  struct sockaddr_un name;
  int sock;
  sock = socket (AF_UNIX, SOCK_STREAM, 0);
  if (sock < 0) {
     // controllare le perror che non creino problemi
     perror ("socket");
     exit (EXIT_FAILURE);
  }
  name.sun_family = AF_UNIX;
  strcpy(name.sun_path, filename);
  if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
     perror("bind");
     exit(EXIT_FAILURE);
  }
  return sock;
}

void suicide(int sig) {
   unlink(PATH);
   exit(GEN_FAIL);
}




/* file sorgente undel.c
 *
 * definizione della funzione del che si occupera di 
 * recuperare un file o un link hard
 *
 */

#include <string.h> 
#include "base-functions.h"

int undel(char *path, char **message) {
   // inodes temporanei
   I_NODE temp_inode;
   I_NODE maybe_purged_inode;
   // offset dell'inode
   int temp_inode_offset;
   // parte sinistra del path
   char *safe_path;
   // parte destra del path
   char *terminal_name;
   // DIRECTORY_ENTRY di appoggio
   DIRECTORY_ENTRY temp_dir_entry;
   // descrittore per il file system virtuale
   int vfs;
   // contatore
   int i;
   
   // se il path non contiene barre (e' malformato) usciamo.
   // Se tutto va bene, mi ricavo la parte destra del path
   if((terminal_name = strrchr(path,'/')) == NULL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // elimino la barra
   terminal_name++;
   
   // controllo se il path esiste
   if((temp_inode_offset = get_i_node(path, &temp_inode)) == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // controllo che abbia l'inode negativo
   if(temp_inode_offset>=0) {
      error_handler(CANNOT_UNDEL_EXISTING_FILE_ERROR_MESSAGE, message);
      return GEN_FAIL;      
   }
      
   // mi ricavo la parte sinistra del path
   safe_path = (char*)malloc((terminal_name - path)+1);
   safe_path[0] = '\0';
   strncat(safe_path, path,(size_t)((terminal_name - path)));
   
   // mi ricavo l'inode della cartella in cui e' contenuto il file da
   // eliminare (dobbiamo modificare la rispettiva directory entry)
   if((temp_inode_offset = get_i_node(safe_path, &temp_inode)) \
                                                       == GEN_FAIL) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // apriamo il file del vfs
   if((vfs = open(PATH,O_RDWR)) == GEN_FAIL) {
      error_handler(OPEN_ERROR_MESSAGE, message);
      return GEN_FAIL;
   }
   
   // ci spostiamo all'offset della directory table
   if(lseek(vfs,temp_inode.block,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // ciclo per trovare l'entry giusta, quando la troviamo usciamo
   // dal ciclo
   i = 0;
   temp_dir_entry.name[0]='\0';

   while(strcmp(temp_dir_entry.name, terminal_name) != 0){
      i++;
      // leggiamo una entry
      if(read(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                       < sizeof(DIRECTORY_ENTRY)) {
         close(vfs);
         return GEN_FAIL;
      }
   }

   // ricordiamo che l'entry giusta deve essere trovata per forza, a
   // causa del controllo all'inizio del listato
   // la modifichiamo rendendo positivo il valore del campo inode
   temp_dir_entry.i_node = - temp_dir_entry.i_node;

   // ma esiste veramente o per caso e' stata fatta una purge?
   if(lseek(vfs,temp_dir_entry.i_node,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   if(read(vfs,&maybe_purged_inode,I_NODE_SIZE) \
                                    < I_NODE_SIZE) {
      close(vfs);
      return GEN_FAIL;
   }

   if(maybe_purged_inode.block == 0) {
      error_handler(INVALID_PATH_MESSAGE, message);
      return GEN_FAIL;
   }

   if(lseek(vfs,((i-1)*sizeof(DIRECTORY_ENTRY))+ temp_inode.block, \
                                                SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }

   // e la riscriviamo
   if(write(vfs,&temp_dir_entry,sizeof(DIRECTORY_ENTRY)) \
                                            < sizeof(DIRECTORY_ENTRY)) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // e vado a scrivere la modifica
   if(lseek(vfs,temp_inode_offset,SEEK_SET) == GEN_FAIL) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   if(write(vfs,&temp_inode,I_NODE_SIZE) < I_NODE_SIZE) {
      error_handler(VFS_INTERNAL_ERROR_MESSAGE, message);
      close(vfs);
      return GEN_FAIL;
   }
   
   // chiudo il file
   close(vfs);
   
   // termino con successo
   (*message)[0] = '0';
   (*message)[1] = '\0';
   
   return EXIT_SUCCESS;
}

/* fine file */




/* file sorgente vd.c
 *
 * sorgente del client 
 * 
 */
 
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#include <stdlib.h>
#include "coderr.h"
#include "const.h"


int send_server(char *param[], int len);

int main(int argc, char *argv[]) {
   // se non ci sono parametri restituisco l'usage
   if(argc == 1) {
      fprintf(stderr,"Usage: %s <comand>\n",argv[0]);
      return -1;
   }
   
   // invio gli argomenti al server e stampo risposta/errore
   if(send_server(argv,argc-1)==-1) {
      return -1;
   }
   
   // se tutto e' andato bene restituisco 0   
   return 0;
}

// comunica al server il comando e gli argomenti da eseguire
// leggendoli carattere per carattere
int send_server(char *param[], int len) {
   int sock;                  // descrittore del socket
   int i,j;                   // variabili per cicli
   char exit_code;            // exit code
   int num_el;                // contatore per la risposta
   char temp = '0';           // buffer da 1 carattere
   char term = '&';           // terminatore
   struct sockaddr_un name;   // struttura per la connessione
   struct timeval timeout;    // timeout per la read
   char *reply;               // array dinamico per la risposta
   
   // inserisco le informazioni nella struttura: tipo e path
   name.sun_family = AF_UNIX;
   strcpy(name.sun_path, SOCK_PATH);
   
   // crea un socket anonimo con socket()
   sock = socket(AF_UNIX, SOCK_STREAM, 0);
      
   // collega il socket al server con connect()
   if(connect(sock, (struct sockaddr *) &name, sizeof(name))==-1) {
      perror("Error connecting to the server");
      return -1;
   }

   // imposto il timeout in ricezione sul socket
   // necessario impostare sia il valore dei secondi...
   timeout.tv_sec = SOCKET_TIMEOUT_VAL;
   // che dei millisecondi
   timeout.tv_usec = 0;
   setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));
 
   // opera sul socket con write()
   for(i = 1; i <= len; i++) {
      temp='0';
      j=0;
      while(temp!='\0') {
         // carica in temp un carattere del comando in input
         temp=param[i][j];
         // lo invia
         if(write(sock,&temp,1)==-1) {
            perror("Error connecting to the server");
            return -1;
         }
         j++;
      }
   }
   
   // invia il carattere di terminazione
   if(write(sock,&term,1)==-1) {
      perror("Error connecting to the server");
      return -1;
   }
   
   // si pone in ascolto sul socket
   // in attesa di risposta da parte del server
   // la read qui puo' dare anche un errore di timeout
   if(read(sock,&exit_code,1)!=1){
      perror("Error/Timeout in receiving reply from server");
      close(sock);
      return -1;
   }
   
   // inizializzo l'array dinamico di char
   reply = (char *)malloc(sizeof(char));
   // inizializzo temp e i per il while
   temp='0';
   i = 0;
   // num_el = 2 cosi' realloc crea un array di 2 elementi alla prima
   // esecuzione del ciclo
   num_el = 2;
   while(temp != '\0'){
      if(read(sock,&temp,1)!=1){
         perror("Error in receiving reply from server");
         close(sock);
         return -1;
      }
      reply[i] = temp;
      i++;
      // rialloco l'array in memoria dinamica 
      reply = (char *)realloc(reply, num_el*sizeof(char));
      num_el++;
   }
   
   if(strlen(reply) != 0) {
      printf("%s\n",reply);
   }
   
   // chiude il socket
   close(sock);
   
   // ovviamo al problema che il primo carattere letto dalla socket
   // non puo' essere negativo e restituiamo il risultato che il
   // comando ha avuto nel server
   if(exit_code=='1')
      return -1;
   
   // se e' andato tutto bene
   return 0;
}




CFLAGS=-Wall -g

all: vd server

clean:
	rm *.o vd server

server:	create-vfs.o \
			allocate-block.o \
			deallocate-block.o \
			get-i-node.o \
			ls.o \
			mkdir.o \
			cp-fs2vd.o \
			cp-vd2fs.o \
			cp-vd2vd.o \
			error-handler.o \
			ln.o \
			purge.o \
			del.o \
			undel.o \
			base-functions.h

vd:	coderr.h \
		const.h

create-vfs.o:	base-functions.h
allocate-block.o:	base-functions.h
deallocate-block.o:	base-functions.h
get-i-node.o:	base-functions.h
mkdir.o:	base-functions.h
ls.o:	base-functions.h
cp-fs2vd.o:	base-functions.h
cp-vd2fs.o:	base-functions.h
cp-vd2vd.o:	base-functions.h
ln.o:	base-functions.h
purge.o:	base-functions.h
del.o:	base-functions.h
undel.o:	base-functions.h

install: 
	script/install.sh

uninstall:
	script/uninstall.sh






Lorenzo Baloci, Daniele Turato, Alessio Zennaro - Settembre 2006