/* floppy2.c
 * Stefano Salvi - 25/9/02
 * Programma che legge il boot sector di un disco (floppy o hard disk) con file system FAT,
 * ne stampa le caratteristiche e stampa la root directory
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/types.h>       
#include <linux/msdos_fs.h>

// Descrittore del disco
struct disco {
  int fd;		// File Descriptor per il dispositivo
  int rootDir;		// posizione in byte della root Directory
  int rootDirSize;	// Dimensione in 'entries' della root directory
};

// Struttura con i campi di bit che descrive la 'data' in formato MS-DOS
struct date {
   unsigned int giorno:5;
   unsigned int mese:4;
   unsigned int anno:7;
};

// Struttura con i campi di bit che descrive l' 'ora' in formato MS-DOS
struct time {
   unsigned int sec:5;
   unsigned int min:6;
   unsigned int ora:5;	//perchè si tengono solo i pari
};


int openDevice (char *device, struct disco *d);		// apre il device, legge boot rec
void readRootDir (struct disco *d);			// Legge l'albero delle directory
int printDirEntry (struct disco *d, int pos);		// Stampa un'entry di dir.

int main(int argc, char **argv)
{
struct disco d;			// Descrittore del dispositivo
char *device = "/dev/fd0";	// Nome del file (dispositivo) da aprire

  if (argc > 1)			// Se c'e' almeno un parametro
  {
    device = argv [1];		// Prende il primo parametro e lo usa come 'dispositivo'
  }

  if(openDevice(device, &d))	// Apre il dispositivo
  {
    return 1;
  }

  readRootDir (&d);
}

/* printDirEntry
 * Stampa un'entry di directory.
 * Riceve il descrittore del dispositivo (d) e la posizione in byte dell'entry.
 * Ritorna la posizione della prossima direntry, oppure 0 se incontra l'ultima
 * direntry (nome che comincia con byte 0x00).
 * Operazioni:
 * - Si posiziona sul record
 * - Legge una 'dir entry'
 * - Se la 'dir entry' ha nome che comincia per 0x00, ritorna 0
 * - Se l'entry non e' 'hidden' o cancellata, la stampa
 * - Ritorna la posizione della prossima dir entry.
 * Non stampa le entry cancellate (che cominciano per 0xe5) o con gli attributi 
 * 'hidden,system,readonly,label' (sono nomi estesi, quindi non stampabili)
 */
int printDirEntry (struct disco *d, int pos)
{
struct msdos_dir_entry dirEntry;// Buffer per il record di directory
struct date *p;			// Record per visualizzare le date
struct time *t;			// Record per visualizzare le ore
int i;

  // Posizionamento sulla dir entry
  if (lseek(d->fd, pos, SEEK_SET) < 0)
  {
    printf ("Errore nel posizionamento al byte %d\n",pos);
    return 0;
  }

  // Lettura dir entry
  if (read(d->fd,&dirEntry,sizeof(dirEntry)) != sizeof(dirEntry))
  {
    printf ("Errore nella lettura della DirEntry al byte %d\n",pos);
    return 0;
  }

  if (dirEntry.name[0] == 0)	// Entry terminatrice della Directory
  {
    return 0;			// Termina il 'chiamante'
  }

  /* Se il file non e' 'hidden,system,readonly,label' (nomi estesi) o comincia 
   *con 0xe5 (file cancellati)
   */
  if (dirEntry.attr != (ATTR_HIDDEN | ATTR_RO | ATTR_SYS | ATTR_VOLUME) && 
    (dirEntry.name[0] & 0xff) != 0xe5)
  {
    /* Analizza bit per bit gli 'attr' e per ognuno stampa una lettera */
    if(dirEntry.attr & ATTR_RO)
    {
      printf("R");
    }
    else
    {
      printf("-");
    }

    if(dirEntry.attr&ATTR_HIDDEN)
    {
      printf("H");
    }
    else
    {
      printf("-");
    }

    if(dirEntry.attr&ATTR_SYS)
    {
      printf("S");
    }
    else
    {
      printf("-");
    }

    if(dirEntry.attr&ATTR_VOLUME)
    {
      printf("L");
    }
    else
    {
      printf("-");
    }

    if(dirEntry.attr&ATTR_DIR)
    {
      printf("D");
    }
    else
    {
      printf("-");
    }

    if(dirEntry.attr&ATTR_ARCH)
    {
      printf("A  ");
    }
    else
    {
      printf("-  ");
    }

    /*informazioni sul file*/
    for(i=0;i<8;i++)			// Otto cratteri del nome
    {
      printf("%c",dirEntry.name[i]);
    }
    printf (".");
    for(i=0;i<3;i++)			// Tre caratteri dell'estensione
    {
      printf("%c",dirEntry.ext[i]);
    }
    p=(struct date *)&(dirEntry.date);	// Sovrappone 'date' a data
    printf(" %d/%02d/%04d ",p->giorno,p->mese,p->anno+1980);
    t=(struct time *)&(dirEntry.time);	// Sovrappone 'time' a time
    printf(" %d:%02d:%02d",t->ora,t->min,t->sec);
    printf(" %d\n",(__u32)dirEntry.size);
  }

  return pos + sizeof (dirEntry);
}

/* readRootDir
 * Stampa la root directory del disco.
 * Riceve come parametro il descrittore del disco.
 * Stampa 'd -> rootDirSize' elementi.
 * Se 'printDirEntry' indica che la directory e' teminata, finisce prima.
 */
void readRootDir (struct disco *d)
{
int pos = d -> rootDir;		// Posizione della directory
int i;				// Contatore entries

  for (i = 0; i < d -> rootDirSize; i++)	// quante ce ne sono al massimo
  {
    if ((pos = printDirEntry (d, pos)) == 0)	// Stampa una dir entry
    {
      break;					// Se e' l'untima, termina
    }
  }
}

/* openDevice
 * Apre il file 'device', ne legge il boot record, compilando il descrittore
 * puntato da 'd'.
 * Ritorna 0 se tutto questo funziona.
 * Ritorna 1 se c'e' un errore.
 */
int openDevice (char *device, struct disco *d)
{
struct fat_boot_sector boot;	// Record nel quale leggere il primo settore

  if((d->fd=open(device,O_RDONLY))==-1)	// Apre il dispositivo
  {
    printf("Errore di apertura! Forse mancano i diritti sul dispositivo\n");
    return 1;
  }

  read(d->fd,&boot,sizeof(boot));	// Legge il boot sector

  // Compila il descrittore del dispositivo
  d->rootDir= (boot.reserved + boot.fats * boot.fat_length) * 
    *(__u16 *)boot.sector_size;			// posizione in byte della root Directory
  d->rootDirSize=*(__u16 *)boot.dir_entries;	// Dimensione in 'entries' della root directory

  // Stampa le caratteristiche interessanti
  printf("\nNum\t\t Description\t\t\t Variabile\n");
  printf("%s\t Volume Name\t\t\t (system_id[8])\n",boot.system_id);
  printf("%d\t\t bytes per logical sector\t (sector_size[2])\n",
    *(__u16 *)boot.sector_size); //dimensione settore  
  printf("%d\t\t sectors/cluster\t\t (cluster_size)\n",boot.cluster_size); //settori per cluster
  printf("%d\t\t reserved sectors\t\t (reserved)\n",boot.reserved);
  printf("%d\t\t number of FATs\t\t\t (fats)\n",boot.fats);
  printf("%d\t\t root directory entries\t\t (dir_entries)\n",*(__u16 *)boot.dir_entries);
  printf("%d\t\t number of sectors\t\t (sectors[2])\n",*(__u16*)boot.sectors);
  printf("%d\t\t media code\t\t\t (media)\n",boot.media);
  printf("%d\t\t sectors/FAT\t\t\t (fat_length)\n",boot.fat_length);
  printf("%d\t\t Start in bytes of ROOT dir\n",d->rootDir);
  printf("%d\t\t sectors per track\t\t (secs_track)\n",(__u16)boot.secs_track);
  printf("%d\t\t number of heads\t\t (heads)\n",boot.heads);
  printf("%d\t\t hidden sectors\t\t\t (hidden)\n",boot.hidden);
  printf("%d\t\t number of sectors\t\t (total_sect)\n\n",boot.total_sect);

  return 0;
}



