AngoLinux

Visalizzazione della directory principale di un filesystem FAT

- A cura del Prof. Stefano Salvi -


Analizziamo altri dati dal filesystem FAT.

Il testo dell'esercizio è il seguente:

Scrivere un programma in C che visualizzi tutte le caratteristiche del dispositivo e del file system lette dal boot sector di un dispositivo o partizione formattato con file system FAT.
Il programma visualizzerà anche la root directory (in un formato simile al DOS), sfruttando le informazioni lette nel boot record.
Utilizzare:
  1. Una struttura che descriva il disco, per comunicare tra le varie funzioni
  2. Una funzione che apra il dispositivo, stampi il boot record e compili la struttura del punto 1
  3. Una funzione che stampi un'entry di directory, a partire dalla posizione in byte sul disco
  4. Una funzione che stampi la root directory.

NOTE:

  • Per accedere al floppy occorre aprire il dispositivo /dev/fd0 con la funzione open per i file non bufferizzati.
    Per avere i diritti su questo dispositivo occorre che il nostro utente sia associato al gruppo floppy.
    Per associare un'utente al gruppo floppy si deve utilizzare il comando gpasswd -a <utente> floppy. Il comando gpasswd richiede i privilegi di root.
  • Per accedere al primo disco fisso occorre aprire il dispositivo /dev/hda con la funzione open per i file non bufferizzati.
    Per avere i diritti su questo dispositivo occorre che il nostro utente sia associato al gruppo disk.
    Per associare un'utente al gruppo disk si deve utilizzare il comando gpasswd -a <utente> disk. Il comando gpasswd richiede i privilegi di root.
  • Per accedere alla prima partizione del primo disco fisso occorre aprire il dispositivo /dev/hda1 con la funzione open per i file non bufferizzati.
    Per avere i diritti su questo dispositivo occorre che il nostro utente sia associato al gruppo disk.
    Per associare un'utente al gruppo disk si deve utilizzare il comando gpasswd -a <utente> disk. Il comando gpasswd richiede i privilegi di root.
  • Per leggere dati da un file non bufferizzato si può usare la funzione read.
  • Per posizionarsi su di un byte di un file non bufferizzato si può usare la funzione lseek. Questa funzione ha un parametro che indica il punto di partenza dello spostamento. Noi faremo tutti spostamenti assoluti, quindi useremo la costante SEEK_SET, per indicare che la posizione indicata è relativa all'inizio del file.
  • Il file header linux/msdos_fs.h contiene la definizione delle strutture utilizzate dal file system FAT. Utilizza alcuni tipi definiti in linux/types.h.
  • In particolare considerando l'inaffidabilità dei floppy disk è indispensabile controllare gli eventuali errori nelle funzioni di posizionamento e lettura.
  • È oportuno stampare le caratteristiche di ogni file su di un'unica riga, per avere una lista facilmente leggibile. Gli attributi potranno essere stampati indicando una lettera (A per archive, S per system ecc.) se un attributo è settato, un - se non è settato.
  • La data e l'ora sono codificati ciascuna in 16 bit, secondo il seguente schema:
    Data
    bit 0..4Giorno
    bit 5..8Mese
    bit 9..15Anno (a partire dal 1980)
    Ora
    bit 0..4Secondi (di due in due)
    bit 5..10Minuti
    bit 11..15Ore
    Per decodificarle si possono utilizzare due metodi:
    • Shift e maschere
    • Struct con i campi di bit
    Consiglio di sperimentare la seconda strada
  • La directory termina quando si legge un'entry il cui nome incomincia con il carattere di vaore 0. Dato che la funzione che stampa la root directory non conoscerà il contenuto della directory entry (che sarà una variabile locale della funzione che la stampa), suggerisco che la funzione di stampa ritorni:
    la posizione della prossima dir entry
    se la dir entry non è l'ultima della directory
    zero
    se il primo carattere del nome di una directory entry vale 0, quindi è l'ultima.
  • Se il primo carattere del nome di una directory entry vale 0xe5, la directory entry è cancellata, quindi non andrà stampata
  • Se attributi di una directory entry sono ATTR_HIDDEN, ATTR_RO, ATTR_SYS e ATTR_VOLUME, la directory entry contiene i caratteri di un nome lungo, quindi non andrà stampata

Una possibile soluzione è la seguente:
/* 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;
}

Per provare il programma, scaricare il sorgente, compilarlo con il comando cc floppy2.c -o floppy2 ed eseguirlo con il comando ./floppy2.


[Home Page dell'ITIS "Fermi"] [Indice Quarta] [Precedente] [Successivo]

© Ing. Stefano Salvi - Released under GPL licence