<- Linux - Copertina - XFSTT ->

Articolo


Il filesystem Ext2

Il filesystem Extended 2 (ext2) è attualmente quello più diffuso in Linux, quello che viene usato normalmente per formattare le partizioni in cui viene installato il sistema. Le principale caratteristiche del filesystem ext2 possono essere riassunte nella seguente tabella:

Come tutti i filesystem unix anche l'ext2 è organizzato in una struttura ben progettata comprendente super_block, inodes, directory e files. Vedremo quindi in dettaglio come sono strutturati i singoli componenti del filesystem ext2, d'ora in poi chiamato semplicemente fs, e quali sono i vantaggi di tale organizzazione.


Il super_block

Lo spazio all'interno di un filesystem unix è organizzato logicamente come un array lineare di blocchi di dimensioni uguali. La dimensione del blocco logico è indipendente dalla dimensione dei blocchi del dispositivo fisico ed è di solito di 1024 bytes, ma può essere fissata anche in 2048 o 4096 bytes. Viceversa il blocco fisico di un normale harddisk è di 512 bytes, quindi un blocco logico occupa di solito 2 blocchi fisici.

In unix ogni filesystem ext2 è descritto da un blocco particolare detto super_block che è memorizzato in posizione fissa all'inizio del fs stesso e ne descrive le caratteristiche, dimensioni, struttura, stato ed altro ancora. Il super_block dell'ext2, chiamato ext2_super_block è descritto dalla seguente struttura:

/*
 * Structure of the super block
 */
struct ext2_super_block {
        __u32   s_inodes_count;         /* Inodes count */
        __u32   s_blocks_count;         /* Blocks count */
        __u32   s_r_blocks_count;       /* Reserved blocks count */
        __u32   s_free_blocks_count;    /* Free blocks count */
        __u32   s_free_inodes_count;    /* Free inodes count */
        __u32   s_first_data_block;     /* First Data Block */
        __u32   s_log_block_size;       /* Block size */
        __s32   s_log_frag_size;        /* Fragment size */
        __u32   s_blocks_per_group;     /* # Blocks per group */
        __u32   s_frags_per_group;      /* # Fragments per group */
        __u32   s_inodes_per_group;     /* # Inodes per group */
        __u32   s_mtime;                /* Mount time */
        __u32   s_wtime;                /* Write time */
        __u16   s_mnt_count;            /* Mount count */
        __s16   s_max_mnt_count;        /* Maximal mount count */
        __u16   s_magic;                /* Magic signature */
        __u16   s_state;                /* File system state */
        __u16   s_errors;               /* Behaviour when detecting errors */
        __u16   s_pad;
        __u32   s_lastcheck;            /* time of last check */
        __u32   s_checkinterval;        /* max. time between checks */
        __u32   s_creator_os;           /* OS */
        __u32   s_rev_level;            /* Revision level */
        __u16   s_def_resuid;           /* Default uid for reserved blocks */
        __u16   s_def_resgid;           /* Default gid for reserved blocks */
        __u32   s_reserved[235];        /* Padding to the end of the block */
};
Si può notare che in esso sono contenuti tre tipi fondamentali di informazioni: Le caratteristiche del filesystem, che ne descrivono la struttura logica, sono stabilite al momento della creazione e non possono in seguito più essere modificate, per esempio la dimensione dei blocchi, il numero di blocchi, il numero di inodes. Una delle caratteristiche non modificabili è ovviamente la dimensione dei blocchi. Il default è 1024 bytes, ma può essere conveniente in certi casi utilizzare una dimensione maggiore. Per esempio utilizzando blocchi da 4096 bytes si riduce il numero di blocchi aggiuntivi necessari ad indirizzare i blocchi di dati dei files, ma si aumenta lo spazio medio non utilizzato nell'ultimo blocco di ciascun file. Una scelta in questo senso può essere vantaggiosa se il filesystem deve contenere files abbastanza grossi, per esempio un server ftp, oppure se viene utilizzato da una applicazione come Postgres, un database relazionale, che legge e scrive sempre pagine di 8192 bytes. In tal caso è conveniente adottare una dimensione del blocco di 4096 bytes, più vicina alla dimensione della pagina.

I parametri modificabili possono invece venire cambiati dal superuser con il programma tune2fs, che permette di variare per esempio il numero massimo di volte che il fs può essere rimontato prima di forzare un fsck automatico, oppure il comportamento in caso di errore. Un interessante parametro modificabile è la percentuale di spazio che può essere riservata ad un utente privilegiato, normalmente root. Questo consente al sistema di continuare a disporre di spazio su disco anche quando il filesystem si riempie per l'uso smodato degli utenti.

Altre informazioni descrivono invece lo stato del filesystem e vengono aggiornate automaticamente man mano che il fs viene usato, per esempio il numero di blocchi liberi, lo stato (montato o meno), il numero di volte che esso è stato montato, ecc.

Una cosa che invece manca totalmente nel super_block è qualsiasi informazione sul tipo e sulla struttura fisica del device o della partizione in cui esso è ospitato. Il filesystem è infatti totalmente indipendente da queste informazioni, che viceversa sono importanti per poter accedere fisicamente alla partizione o al device. Il fs si basa infatti su un modello lineare della partizione sottostante che viene vista come un insieme di blocchi omogenei demandando al device driver tutte le operazioni necessarie per accedere ad un singolo blocco logico. Un fs può addirittura essere contenuto in un normale file contenuto in un altro filesystem e in questo caso parlere di tracce e cilindri sarebbe completamente privo di senso. Per questo motivo tali informazioni non sono memorizzate all'interno del fs. Per confronto vediamo invece cosa contiene un boot record del dos, l'analogo per certi versi del super_block di ext2:

struct msdos_boot_sector {
        __s8    ignored[3];     /* Boot strap short or near jump */
        __s8    system_id[8];   /* Name - can be used to special case
                                   partition manager volumes */
        __u8    sector_size[2]; /* bytes per logical sector */
        __u8    cluster_size;   /* sectors/cluster */
        __u16   reserved;       /* reserved sectors */
        __u8    fats;           /* number of FATs */
        __u8    dir_entries[2]; /* root directory entries */
        __u8    sectors[2];     /* number of sectors */
        __u8    media;          /* media code (unused) */
        __u16   fat_length;     /* sectors/FAT */
        __u16   secs_track;     /* sectors per track */
        __u16   heads;          /* number of heads */
        __u32   hidden;         /* hidden sectors (unused) */
        __u32   total_sect;     /* number of sectors (if sectors == 0) */
};
Si può vedere innanzitutto che ci sono molte meno informazioni che nell'ext2, per esempio manca un magic number che mi permetta di identificare univocamente una partizione dos come tale o distinguere un fs msdos da uno di tipo vfat. Inoltre si può notare che nel boot record sono mescolate fra loro informazioni sulla geometria del disco (media, heads, secs_track, ecc.), informazioni sulla struttura del filesystem (cluster_size, fat_length, sectors, ecc.) e perfino una variabile `ignored' che contiene nientemeno che del codice eseguibile per 8086. Un bel casino, nel tipico stile Microsoft.

Tutto questo significa per esempio che non posso copiare brutalmente una partizione dos da un disco a un' altro se la geometria dei due dischi è diversa. Inoltre la mancanza di informazioni sullo stato del fs, la sua semplificazione e la scarsa ridondanza delle sue strutture dati rendono molto più problematica la recovery di eventuali errori.


I Cylinder Groups

Il filesystem ext2 è suddiviso logicamente in più parti, dette cylinder groups, che vengono gestite come entità separate ed autonome pur facendo parte dello stesso fs. Lo scopo di questa suddivisione è duplice. Per prima cosa si vogliono minimizzare le conseguenze di eventuali errori, cioè fare sì che se alcuni dati di un cylinder group risultano corrotti il danno resti limitato all'interno del cylinder group stesso e non si propaghi a tutto il fs. Il concetto è un po' analogo a quallo dei compartimenti stagni dei sommergibili, se uno si buca il sommergibile non affonda. Il secondo motivo è invece la tendenza a localizzare i files nell'intorno delle loro directory per ridurre i tempi di accesso, cosa che viene ottenuta cercando di allocare inodes e blocchi nello stesso cylinder group delle directory. Per ridurre ultriormente le possibilità che un intero filesystem venga corrotto a causa di eventuali errori, sia il super_block che le group descriptor tables vengono duplicati in ogni cylinder group, come mostrato in figura:

In questo modo se uno dei super_block o group descriptor viene corrotto a causa di qualche errore esso può essere facilmente ripristinato a partire da una delle sue copie. Abbiamo quindi una struttura altamente ridondante che permette la recovery di eventuali errori.

Lo spazio all'interno del fs è gestito separatamente ed autonomamente per ciascun cylinder-group. Come si vede dalla figura ciascun cylinder group contiene una block bitmap, che indica quali blocchi del cylinder group sono stati allocati a files o directories, una inode bitmap, che indica analogamente quali inode risultano allocati, ed una inode table che contiene gli inodes appartenenti al cylinder group. Ciascuna bitmap è organizzata come una sequenza di bits ognuno dei quali indica se il corrispondente inode o blocco è libero o occupato. Ciascuna bitmap occupa esattamente un blocco e quindi il numero massimo di inodes o di blocchi che possono essere contenuti in un cylinder group è data dalla dimensione in bytes di un blocco moltiplicata per 8, per esempio in un fs con blocchi da 1K ci sono 8192 (1024 * 8) blocchi per cylinder group, poiché la block bitmap può contenere al massimo 8192 bits.

Dato che i blocchi e gli inodes sono ripartiti nei vari cylinder group, per individuare la posizione di uno di essi all'interno del fs a partire dal suo numero occorre prima individuare il cylinder group, dividendo il numero per il numero di oggetti per cylinder group e quindi individuare l'oggetto all'interno del cylinder group utilizzando come indice il resto di tale divisione. Per esempio volendo accedere all'inode 17654 di un fs con 1016 inodes per group si ottiene:

  group = 17654 / 1016 = 17
  index = 17654 % 1016 = 382
e quindi si deve cercare l'inode 382 del cylinder group 17. Ciascun gruppo contiene solo una parte degli inodes e dei blocchi di tutto il fs e l'indice ottenuto vale solo all'interno del gruppo. Dal punto di vista dell'utente il filesystem è visto come un tutt'uno e la sua suddivisione in cylinder-groups è totalmente trasparente.

Ciascun cylinder-group è descritto da un apposito blocco di controllo detto group descriptor, che ha la seguente struttura:

struct ext2_group_desc {
        __u32   bg_block_bitmap;        /* Blocks bitmap block */
        __u32   bg_inode_bitmap;        /* Inodes bitmap block */
        __u32   bg_inode_table;         /* Inodes table block */
        __u16   bg_free_blocks_count;   /* Free blocks count */
        __u16   bg_free_inodes_count;   /* Free inodes count */
        __u16   bg_used_dirs_count;     /* Directories count */
        __u16   bg_pad;
        __u32   bg_reserved[3];
};

Il group descriptor contiene, oltre ai puntatori alle bitmap di allocazione ed alla tabella degli inodes, anche dei contatori (bg_free_blocks_count, bg_free_inodes_count e bg_used_dirs_count) che sono aggiornati ed utilizzati dalle routines di allocazione del fs. In particolare bg_used_dirs_count viene utilizzato durante la creazione di una nuova directory per ripartire uniformemente le directory fra i vari gruppi, in modo che possa essere applicato il principio della località dei dati, cioè che in ciascun gruppo ci possa essere spazio per allocare gli inodes ed i blocchi dei files contenuti nelle directories del gruppo. Ovviamente se ciò non è possibile inodes e blocchi vengono allocati anche in altri gruppi dove c'è dello spazio disponibile. La località dei dati è fondamentale per ottenere buone prestazioni dal fs stesso. Per esempio quando si fa un ls -l è necessario per ogni entry della directory che si sta listando accedere anche all'inode corrispondente ed è quindi molto importante che esso sia nelle immediate vicinanze della directory stessa per poter ridurre i tempi di accesso al disco. Lo stesso principio vale ovviamente quando si vuole leggere i blocchi di dati di un file contenuto in una certa directory.


Gli Inodes

L'inode è la risorsa principale di un filesystem unix. Ad ogni file o directory è associato univocamente un inode che ne identifica le caratteristiche ed indica dove sono fisicamente memorizzati i dati. Tutte le operazioni su un file o una directory vengono effettuate tramite il suo inode, che contiene tutte le informazioni sul file stesso, esclusi i dati veri e propri, come sintetizzato dalla seguente figura:

Gli inodes sono numerati progressivamente da 1 in su ed il loro numero è l'informazione utilizzata dal kernel per accedere all'inode e quindi ai dati del file corrispondente. La struttura di un inode di un fs ext2 è la seguente:

struct ext2_inode {
        __u16   i_mode;         /* File mode */
        __u16   i_uid;          /* Owner Uid */
        __u32   i_size;         /* Size in bytes */
        __u32   i_atime;        /* Access time */
        __u32   i_ctime;        /* Creation time */
        __u32   i_mtime;        /* Modification time */
        __u32   i_dtime;        /* Deletion Time */
        __u16   i_gid;          /* Group Id */
        __u16   i_links_count;  /* Links count */
        __u32   i_blocks;       /* Blocks count */
        __u32   i_flags;        /* File flags */
        union {
                struct {
                        __u32  l_i_reserved1;
                } linux1;
                struct {
                        __u32  h_i_translator;
                } hurd1;
                struct {
                        __u32  m_i_reserved1;
                } masix1;
        } osd1;                         /* OS dependent 1 */
        __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
        __u32   i_version;      /* File version (for NFS) */
        __u32   i_file_acl;     /* File ACL */
        __u32   i_dir_acl;      /* Directory ACL */
        __u32   i_faddr;        /* Fragment address */
        union {
                struct {
                        __u8    l_i_frag;       /* Fragment number */
                        __u8    l_i_fsize;      /* Fragment size */
                        __u16   i_pad1;
                        __u32   l_i_reserved2[2];
                } linux2;
                struct {
                        __u8    h_i_frag;       /* Fragment number */
                        __u8    h_i_fsize;      /* Fragment size */
                        __u16   h_i_mode_high;
                        __u16   h_i_uid_high;
                        __u16   h_i_gid_high;
                        __u32   h_i_author;
                } hurd2;
                struct {
                        __u8    m_i_frag;       /* Fragment number */
                        __u8    m_i_fsize;      /* Fragment size */
                        __u16   m_pad1;
                        __u32   m_i_reserved2[2];
                } masix2;
        } osd2;                         /* OS dependent 2 */
};
Come si vede ci sono molte informazioni che descrivono le caratteristiche di un file, alcune delle quali relative a funzionalità previste ma al momento non ancora utilizzate, per esempio acl e fragment.

Il primo campo, i_mode contiene le permissions ed il tipo di file ed è suddiviso nel seguente modo:

I campi i_uid e i_gig contengono rispettivamente l'userid ed il groupid del proprietario del file, rappresentati come interi di 16 bit.

Il campo i_size contiene la dimensione in bytes del file oppure, nel caso si tratti di un device, un numero di 16 bit che specifica il major e il minor number del device stesso. Per esempio le partizioni del primo harddisk ide hanno major number 3 e minor number che va da 1 a 9:

  $ ls -l /dev/hda[1-9]
  brw-rw----   1 root     disk    3,  1 Apr 28  1995 /dev/hda1
  brw-rw----   1 root     disk    3,  2 Apr 28  1995 /dev/hda2
  brw-rw----   1 root     disk    3,  3 Apr 28  1995 /dev/hda3
  brw-rw----   1 root     disk    3,  4 Apr 28  1995 /dev/hda4
  brw-rw----   1 root     disk    3,  5 Apr 28  1995 /dev/hda5
  brw-rw----   1 root     disk    3,  6 Apr 28  1995 /dev/hda6
  brw-rw----   1 root     disk    3,  7 Apr 28  1995 /dev/hda7
  brw-rw----   1 root     disk    3,  8 Apr 28  1995 /dev/hda8
  brw-rw----   1 root     disk    3,  9 Apr 28  1995 /dev/hda9
Quando si accede ad un device, sempre tramite il suo inode, il major e minor number vengono utilizzati dal kernel per sapere che classe di device e quale particolare dispositivo all'interno di quella classe si sta utilizzando, in modo da individuare il particolare driver all'interno del kernel in grado di accedere fisicamente allo specifico hardware. Per esempio il file /dev/tty2, con major e minor 4,2 è contenuto in un fs nell'harddisk, ma essendo un character device i suoi dati non vengono letti dall'harddisk ma dal device numero 2 (tty2) del driver numero 4 (ttyp), cioè dalla console numero 2 del nostro pc.

I quattro campi i_atime, i_ctime, i_mtime, e i_dtime contengono rispettivamente i tempi di ultimo accesso, creazione, modifica e cancellazione del file, espressi in secondi dall'1-1-1970 secondo lo standard unix.

Il campo i_links_count contiene il numero di hard-links che puntano all'inode. Il suo valore è di solito 1, ma può essere maggiore di 1 se vengono creati degli ulteriori hard-links all'inode.

Il campo i_blocks indica il numero di blocchi occupati dal file, compresi anche i blocchi di indirizzamento indiretto che verranno illustrati più avanti. E' da notare che i campi i_size ed i_blocks possono indicare dimensioni del file differenti, dato che in unix è possibile creare i cosiddetti sparse files cioè dei files in cui non tutti i blocchi necessari a raggiungere la dimensione specificata risultano allocati, in altre parole dei files coi buchi. Nella figura seguente è illustrato un file sparso di 7000 bytes che però occupa solo 3 blocchi da 1024 bytes. Il file ha allocati solo i blocchi di dati corrispondenti agli offset 0, 2048 e 6144, e quindi una lettura fatta dopo una seek ad una posizione non allocata ritorna semplicemente degli zeri. Il vantaggio è che pur avendo un file che nominalmente è di 7000 bytes posso allocare solo i 3KB corrispondenti alle posizioni in cui ho dati significativi:

I file sparsi sono utilizzati normalmente per i dump di memoria dei programmi che vanno in errore, in cui si vuole avere un file con uno spazio di indirizzamento corrispondente alla memoria virtuale del processo in errore ma allocando su disco solo i blocchi corrispondenti alle pagine di memoria che erano state effettivamente allocate al processo. I files sparsi non possono essere implementati nel fs dos perché questo utilizza un sistema di allocazione dei cluster a liste concatenate che non permette di indicare dei buchi nell'allocazione dei blocchi. Per visualizzare la quantità di blocchi effettivamente utilizzata da un file si può usare l'opzione -s del comando ls, che indica in prima colonna il numero di blocchi allocati per ciascun file:
  $ ls -ls pippo pluto
  3 -rw-r--r--   1 dz   users   2500 Apr 23 13:55 pippo
  0 lrwxrwxrwx   1 dz   users      5 Apr 23 13:55 pluto -> pippo

Il campo i_flags contiene dei flags aggiuntivi che indicano particolari caratteristiche del file:

Non tutte le funzionalità indicate da questi flags sono attualmente implementate nella versione corrente del fs ext2, in particolare l'undelete dei files e la compressione automatica non sono disponibili, ma è comunque previsto che il filesystem possa in futuro essere esteso con nuove funzionalità. Per quanto riguarda gli altri bits, secure deletion indica che quando il file viene cancellato i suoi blocchi devono essere riempiti di zeri, il synchronous update indica che i suoi dati devono essere immediatamente scritti su disco senza farli attendere in cache, immutable significa che il file non puà essere modificato, cancellato, rinominato e non possono essere aggiunti degli hard-link. Append only indica che nuovi dati possono essere scritti solo in coda al file, do not dump significa che il file non deve essere salvato dai programmi di dump. I flags di un file possono essere modificati con il comando chattr, che però può essere eseguito solo dal superuser.

I campi i_file_acl e i_dir_acl non sono attualmente usati ma sono previsti per implementare il controllo sull'accesso al file tramite Access Control List (ACL).

Anche i campi l_faddr, l_i_frag e l_i_fsize non sono usati attualmente, ma sono comunque presenti nell'inode per poter permettere in futuro di implementare la gestione dei frammenti di blocchi, consentendo così di ottimizzare l'occupazione di spazio su disco delle code dei files, che attualmente occupano un intero blocco anche se devono contenere solo pochi bytes.

Nell'inode troviamo anche un array i_blocks di 15 elementi che contiene i puntatori ai blocchi di dati del file. I primi 12 elementi, da i_blocks[0] a i_blocks[11], sono detti direct blocks e contengono i numeri dei primi 12 blocchi del file. L'elemento successivo, i_blocks[12], punta invece ad un blocco, detto indirect block, che non contiene dati del file ma un array di puntatori ai blocchi di dati. Assumendo che ciascun blocco abbia dimensione pari a 1024 bytes esso potrà contenere 256 (1024 / 4) puntatori a blocchi di dati ed sarà pertanto in grado di indirizzare i blocchi da 12 a 268. L'elemento i_blocks[14] punta ad un altro blocco, detto double indirect block, che punta ad un blocco di puntatori a blocchi di puntatori, in grado quindi di indirizzano i 256 * 256 blocchi di dati che vanno da 269 a 65804. Troviamo infine i_blocks[15], detto triple indirect block, che aggiunge un ulteriore livello di indirezione consentendo l'indirizzamento di altri 256 * 256 * 256 blocchi di dati oltre il 65804. Ovviamente tutti questi blocchi indiretti vengono allocati solo se sono effettivamente necessari per indirizzare dei blocchi di dati. Il tutto può essere rappresentato graficamente così:

Il vantaggio di questa struttura è che per files piccoli non devo allocare spazio aggiuntivo per contenere i puntatori ai dati, dato che per i primi 12K sono sufficienti i puntatori diretti contenuti nell'inode. In questo modo si risparmia anche l'accesso al disco che sarebbe necessario per leggere tale blocco. Man mano che il file cresce di dimensioni è invece necessario introdurre livelli successivi di indirizzamento indiretto per contenere i puntatori ai dati. Confrontando questa organizzazione con quella del dos si può notare che distribuendo i puntatori ai dati nelle vicinanze dei dati stessi si migliorano i tempi di accesso, mentre in dos è sempre necessario muovere le testine del disco all'inizio della partizione per poter leggere i puntatori ai cluster contenuti nella FAT.

Una cosa importante da notare è che l'allocazione dello spazio per i dati non è fatta "come capita", come succede con dos e windows, ma secondo un algoritmo che cerca di utilizzare blocchi contigui minimizzando così la frammentazione dei dati che è invece tipica dei sistemi operativi più primitivi. Questo è possibile perch´ ext2 utilizza dei contatori che indicano in ogni istante quanto spazio libero c'è per ogni cylinder-group e delle bitmap di allocazione che permettono di cercare velocemente dove conviene allocare nuovo spazio per i dati. Contatori analoghi vengono mantenuti per inodes e directories in modo da poter distribuire i dati uniformemente per tutto il filesystem.

Di tutti gli inodes che vengono creati in un filesystem quando viene formattato alcuni sono riservati e non possono essere allocati a files degli utenti. Gli inodes riservati dell'ext2 sono i seguenti:

Il primo inode disponibile è pertanto l'inode 11 e questo viene di solito allocato alla directory lost+found, una directory particolare preallocata dall'mke2fs per consentire all'e2fsck di salvare al suo interno eventuali inodes che risultassero non collegati a nessun file, quelli che in dos sono i lost cluster e vengono salvati dal chkdsk nella directory C:\. Gli inodes a partire da 12 sono quindi allocati ai files e directory degli utenti.


La directory

Come già visto una directory è un file come tutti gli altri, con l'unica differenza che i dati in esso contenuti sono le informazioni sui files nella directory, e viene pertanto gestito in modo particolare dal filesystem e dal kernel. Ciascuna entry di directory è costituita da un record di lunghezza variabile allineata alla word (4 bytes) contente solamente il nome del file ed il numero di inode corrispondente. Tutte le altre informazioni sul file non hanno niente a che fare con la directory in cui esso è contenuto e sono pertanto memorizzate nel suo inode. La directory serve solo a collegare il nome del file col suo inode. La directory entry di ext2 è descritta dalla seguente struttura:

#define EXT2_NAME_LEN 255
struct ext2_dir_entry {
        __u32   inode;                  /* Inode number */
        __u16   rec_len;                /* Directory entry length */
        __u16   name_len;               /* Name length */
        char    name[EXT2_NAME_LEN];    /* File name */
};
Il tutto si può anche rappresentare graficamente così:

Se esaminiamo invece una entry di una directory msdos possiamo vedere che essa è di lunghezza fissa e contiene al suo interno anche tutte quelle informazioni (poche per la verità) che in unix si è invece preferito memorizzare separatamente nell'inode. Si può vedere che il dos gestisce solo la data e ora di ultima modifica, che mancano totalmente i concetti di utente e di permessi di accesso, e che ovviamente il nome del file è di soli 8+3 caratteri, ovviamente tutti rigorosamente maiuscoli:

struct msdos_dir_entry {
        __s8    name[8],ext[3]; /* name and extension */
        __u8    attr;           /* attribute bits */
        __u8    unused[10];
        __u16   time,date,start;/* time, date and first cluster */
        __u32   size;           /* file size (in bytes) */
};
Non vi descrivo come sono implementati i nomi lunghi nel filesystem di tipo vfat utlizzato da Windows95 perché ne sareste assolutamente inorriditi. Vi basti sapere che il vfat non è altro che un normale msdos un po' accrocchiato per farlo sembrare una novità. In effetti sul vfat ci gira tranquillamente anche il vecchio dos, quindi tanto nuovo non può essere.


Fast Symbolic Link

Come tutti sanno in unix è possibile creare dei link simbolici ad un file, cioè utilizzare un file con un certo path per fare in realtà riferimento ad un file che sta da un'altra parte, al limite su un altro disco o su un altro computer. Esisono anche i link fisici ma questi non sono altro che diverse entry di directory che puntano allo stesso inode. Un link simbolico a un file è di solito implementato memorizzando il pathname del file destinazione in un blocco di dati associato all'inode del link stesso.

Nel filesystem ext2 in realtà non è sempre così poiché se il pathname destinazione è lungo non più di 59 bytes esso viene memorizzato direttamente nell'array i_blocks al posto dei puntatori ai blocchi di dati. Così facendo si risparmiano l'allocazione al blocco di dati e l'accesso al disco necessario per leggere il pathname del target, dato che questo è già disponibile nell'inode. I symlinks di questo tipo sono per questo motivo chiamati Fast Symbolic Link e ciascuno di essi occupa un inode ma nessun blocco di dati. Se invece il pathname del target è più lungo di 60 bytes deve essere allocato un blocco di dati come per un file normale. Nella figura seguente sono rappresentati i due tipi di symlink:

Di fatto quasi tutti i link simbolici presenti in un filesystem ext2 sono dei Fast Symlink, dato che nella realtà non si usano mai pathnames lunghi più di 59 caratteri.

Si può anche vedere che esiste in teoria una ulteriore possibilità di ottimizzazione del fs ext2 che consisterebbe nell'implementazione dei cosiddetti Fast Files, cioè di quei files di lunghezza minore o uguale di 59 bytes il cui contenuto potrebbe essere memorizzato direttamente nell'inode al posto dei puntatori ai blocchi, analogamente a quanto avviene per i Fast Symlink. Al momento questo non è implementato ma in teoria si potrebbe fare. Poiché il numero di inodes di un filesystem è sempre molto sovradimensionato rispetto alla quantità effettivamente necessaria si può concludere che creare un Fast Symlink (o un ipotetico Fast File) costa molto poco in termini di spazio occupato su disco dato che l'unico spazio allocato è quello occupato dalla entry di directory.

di Massimo Dal Zotto


<- Linux - Copertina - XFSTT ->