print · rss · source

< Lire et écrire sur un dique IDE | TutoOS | Créer et lancer une application au format ELF à partir du système de fichier >


Utiliser un système de fichier Ext2FS

Les sources

Le package contenant les sources est téléchargeable ici : kernel_Ext2Read.tgz
Pour naviguer dans l'arborescence : Ext2Read


Description d'un système de fichier Ext2FS

Un disque subdivisé en groupes

Quand on dépose sur un disque un système de fichiers de type Ext2, on inscrit sur celui-ci une structure particulière afin de stocker et d'organiser des données. La première partie du disque, de 1024 octets, est réservée au secteur de boot. Le reste du disque est divisé en groupes. Chaque groupe contient un en-tête et une zone pour les données :

L'en-tête de chaque groupe contient notamment les informations fondamentales décrivant la structure globale du système de fichiers. Chaque groupe possède une copie de ces informations :

  • Le superbloc contient les informations générales décrivant l'organisation logique du disque.
  • L'espace group descriptors est un tableau contenant l'ensemble des descripteurs de groupe. Pour chaque groupe, un descripteur contient les informations décrivant l'organisation spécifique des données en son sein.

Le reste de l'en-tête contient les structures décrivant l'organisation des données spécifiques à ce groupe :

  • Le bloc bitmap indique pour chaque bloc de ce groupe si il est libre ou utilisé
  • Le bloc inode bitmap indique pour chaque inode de ce groupe si elle est libre ou utilisée
  • L'espace inode table contient la liste des inodes de ce groupe

Lecture dans un système de fichier Ext2FS

Au préalable, le noyau doit récupérer les informations décrivant l'organisation globale des données sur le disque. Ces données sont consignées dans le superbloc et dans le tableau de descripteurs de groupes. Ensuite, en se servant de son numéro d'inode, il n'est pas si difficile que ça de retrouver les informations relatives à un fichier et d'en lire le contenu.

Astuce : les fonctions de manipulation du système de fichier peuvent être testées dans l'espace utilisateur sous Unix avant d'être implémentées au niveau du noyau. Pour ne pas risquer de corrompre un disque logique utilisé par le système, il est plus prudent de créer un disque virtuel dans un fichier qui servira pour l'occasion.

Le superbloc

Le superbloc contient les informations relatives à l'organisation globale du système de fichiers, sa structure est détaillée dans le fichier ext2.h. Le superbloc fait une taille de 1024 octets. Si la taille des blocs est supérieure à 1024 ko, du padding est ajouté afin que l'espace group descriptors soit aligné sur cette taille de bloc. L'offset de la zone group descriptor dépend de la taille des blocs :

#include "types.h"

struct ext2_super_block {
        u32 s_inodes_count;     /* Total number of inodes */
        u32 s_blocks_count;     /* Total number of blocks */
        u32 s_r_blocks_count;   /* Total number of blocks reserved for the super user */
        u32 s_free_blocks_count;        /* Total number of free blocks */
        u32 s_free_inodes_count;        /* Total number of free inodes */
        u32 s_first_data_block; /* Id of the block containing the superblock structure */
        u32 s_log_block_size;   /* Used to compute block size = 1024 << s_log_block_size */
        u32 s_log_frag_size;    /* Used to compute fragment size */
        u32 s_blocks_per_group; /* Total number of blocks per group */
        u32 s_frags_per_group;  /* Total number of fragments per group */
        u32 s_inodes_per_group; /* Total number of inodes per group */
        u32 s_mtime;            /* Last time the file system was mounted */
        u32 s_wtime;            /* Last write access to the file system */
        u16 s_mnt_count;        /* How many `mount' since the last was full verification */
        u16 s_max_mnt_count;    /* Max count between mount */
        u16 s_magic;            /* = 0xEF53 */
        u16 s_state;            /* File system state */
        u16 s_errors;           /* Behaviour when detecting errors */
        u16 s_minor_rev_level;  /* Minor revision level */
        u32 s_lastcheck;        /* Last check */
        u32 s_checkinterval;    /* Max. time between checks */
        u32 s_creator_os;       /* = 5 */
        u32 s_rev_level;        /* = 1, Revision level */
        u16 s_def_resuid;       /* Default uid for reserved blocks */
        u16 s_def_resgid;       /* Default gid for reserved blocks */
        u32 s_first_ino;        /* First inode useable for standard files */
        u16 s_inode_size;       /* Inode size */
        u16 s_block_group_nr;   /* Block group hosting this superblock structure */
        u32 s_feature_compat;
        u32 s_feature_incompat;
        u32 s_feature_ro_compat;
        u8 s_uuid[16];          /* Volume id */
        char s_volume_name[16]/* Volume name */
        char s_last_mounted[64];        /* Path where the file system was last mounted */
        u32 s_algo_bitmap;      /* For compression */
        u8 s_padding[820];
} __attribute__ ((packed));

struct disk {
        int device;
        struct ext2_super_block *sb;
        u32 blocksize;
        u16 groups;             /* Total number of groups */
        struct ext2_group_desc *gd;
};

struct ext2_group_desc {
        u32 bg_block_bitmap;    /* Id of the first block of the "block bitmap" */
        u32 bg_inode_bitmap;    /* Id of the first block of the "inode bitmap" */
        u32 bg_inode_table;     /* Id of the first block of the "inode table" */
        u16 bg_free_blocks_count;       /* Total number of free blocks */
        u16 bg_free_inodes_count;       /* Total number of free inodes */
        u16 bg_used_dirs_count; /* Number of inodes allocated to directories */
        u16 bg_pad;             /* Padding the structure on a 32bit boundary */
        u32 bg_reserved[3];     /* Future implementation */
} __attribute__ ((packed));

struct ext2_inode {
        u16 i_mode;             /* File type + access rights */
        u16 i_uid;
        u32 i_size;
        u32 i_atime;
        u32 i_ctime;
        u32 i_mtime;
        u32 i_dtime;
        u16 i_gid;
        u16 i_links_count;
        u32 i_blocks;           /* 512 bytes blocks ! */
        u32 i_flags;
        u32 i_osd1;

        /*
         * [0] -> [11] : block number (32 bits per block)
         * [12]        : indirect block number
         * [13]        : bi-indirect block number
         * [14]        : tri-indirect block number
         */

        u32 i_block[15];

        u32 i_generation;
        u32 i_file_acl;
        u32 i_dir_acl;
        u32 i_faddr;
        u8 i_osd2[12];
} __attribute__ ((packed));

struct directory_entry {
        u32 inode;              /* inode number or 0 (unused) */
        u16 rec_len;            /* offset to the next dir. entry */
        u8 name_len;            /* name length */
        u8 file_type;
        char name;
} __attribute__ ((packed));


/* super_block: s_errors */
#define EXT2_ERRORS_CONTINUE    1
#define EXT2_ERRORS_RO          2
#define EXT2_ERRORS_PANIC       3
#define EXT2_ERRORS_DEFAULT     1

/* inode: i_mode */
#define EXT2_S_IFMT     0xF000  /* format mask  */
#define EXT2_S_IFSOCK   0xC000  /* socket */
#define EXT2_S_IFLNK    0xA000  /* symbolic link */
#define EXT2_S_IFREG    0x8000  /* regular file */
#define EXT2_S_IFBLK    0x6000  /* block device */
#define EXT2_S_IFDIR    0x4000  /* directory */
#define EXT2_S_IFCHR    0x2000  /* character device */
#define EXT2_S_IFIFO    0x1000  /* fifo */

#define EXT2_S_ISUID    0x0800  /* SUID */
#define EXT2_S_ISGID    0x0400  /* SGID */
#define EXT2_S_ISVTX    0x0200  /* sticky bit */
#define EXT2_S_IRWXU    0x01C0  /* user access rights mask */
#define EXT2_S_IRUSR    0x0100  /* read */
#define EXT2_S_IWUSR    0x0080  /* write */
#define EXT2_S_IXUSR    0x0040  /* execute */
#define EXT2_S_IRWXG    0x0038  /* group access rights mask */
#define EXT2_S_IRGRP    0x0020  /* read */
#define EXT2_S_IWGRP    0x0010  /* write */
#define EXT2_S_IXGRP    0x0008  /* execute */
#define EXT2_S_IRWXO    0x0007  /* others access rights mask */
#define EXT2_S_IROTH    0x0004  /* read */
#define EXT2_S_IWOTH    0x0002  /* write */
#define EXT2_S_IXOTH    0x0001  /* execute */

struct disk *ext2_get_disk_info(int);
struct ext2_super_block *ext2_read_sb(int);
struct ext2_group_desc *ext2_read_gd(struct disk *);

struct ext2_inode *ext2_read_inode(struct disk *, int);
char *ext2_read_file(struct disk *, struct ext2_inode *);

A partir des informations contenues dans le superbloc, on obtient des informations qui sont primordiales pour comprendre l'organisation globale du système de fichier :

  • La taille des blocs : 1024 << s_log_block_size
  • La taille des inodes : s_inode_size
  • Le nombre de groupes, il faut prendre le maximum de :
    • s_blocks_count / s_blocks_per_group + ((s_blocks_count % s_blocks_per_group) ? 1 : 0)
    • s_inodes_count / s_inodes_per_group + ((s_inodes_count % s_inodes_per_group) ? 1 : 0)

La fonction ext2_read_sb() du fichier ext2.c lit le superbloc et calcule à partir de ses informations la taille des blocs et le nombre de groupes.

#include "ext2.h"
#include "disk.h"
#include "kmalloc.h"
#include "lib.h"


/*
 * Initialise la structure decrivant le disque logique.
 * Offset correspond au debut de la partition.
 */

struct disk *ext2_get_disk_info(int device)
{
        int i, j;
        struct disk *hd;

        hd = (struct disk *) kmalloc(sizeof(struct disk));

        hd->device = device;
        hd->sb = ext2_read_sb(device);
        hd->blocksize = 1024 << hd->sb->s_log_block_size;

        i = (hd->sb->s_blocks_count / hd->sb->s_blocks_per_group) +
            ((hd->sb->s_blocks_count % hd->sb->s_blocks_per_group) ? 1 : 0);
        j = (hd->sb->s_inodes_count / hd->sb->s_inodes_per_group) +
            ((hd->sb->s_inodes_count % hd->sb->s_inodes_per_group) ? 1 : 0);
        hd->groups = (i > j) ? i : j;

        hd->gd = ext2_read_gd(hd);

        return hd;
}

struct ext2_super_block *ext2_read_sb(int device)
{
        struct ext2_super_block *sb;

        sb = (struct ext2_super_block *) kmalloc(sizeof(struct ext2_super_block));
        disk_read(device, 1024, (char *) sb, sizeof(struct ext2_super_block));

        return sb;
}

struct ext2_group_desc *ext2_read_gd(struct disk *hd)
{
        struct ext2_group_desc *gd;
        int offset, gd_size;

        /* localisation du bloc */
        offset = (hd->blocksize == 1024) ? 2048 : hd->blocksize;

        /* taille occupee par les descripteurs */
        gd_size = hd->groups * sizeof(struct ext2_group_desc);

        /* creation du tableau de descripteurs */
        gd = (struct ext2_group_desc *) kmalloc(gd_size);

        disk_read(hd->device, offset, (char *) gd, gd_size);

        return gd;
}

/* Retourne la structure d'inode a partir de son numero */
struct ext2_inode *ext2_read_inode(struct disk *hd, int i_num)
{
        int gr_num, index, offset;
        struct ext2_inode *inode;

        inode = (struct ext2_inode *) kmalloc(sizeof(struct ext2_inode));

        /* groupe qui contient l'inode */
        gr_num = (i_num - 1) / hd->sb->s_inodes_per_group;

        /* index de l'inode dans le groupe */
        index = (i_num - 1) % hd->sb->s_inodes_per_group;

        /* offset de l'inode sur le disk */
        offset =
            hd->gd[gr_num].bg_inode_table * hd->blocksize + index * hd->sb->s_inode_size;

        /* lecture */
        disk_read(hd->device, offset, (char *) inode, hd->sb->s_inode_size);

        return inode;
}

char *ext2_read_file(struct disk *hd, struct ext2_inode *inode)
{
        char *mmap_base, *mmap_head, *buf;

        int *p, *pp, *ppp;
        int i, j, k;
        int n, size;

        buf = (char *) kmalloc(hd->blocksize);
        p = (int *) kmalloc(hd->blocksize);
        pp = (int *) kmalloc(hd->blocksize);
        ppp = (int *) kmalloc(hd->blocksize);

        /* taille totale du fichier */
        size = inode->i_size;
        mmap_head = mmap_base = kmalloc(size);

        /* direct block number */
        for (i = 0; i < 12 && inode->i_block[i]; i++) {
                disk_read(hd->device, inode->i_block[i] * hd->blocksize, buf, hd->blocksize);

                n = ((size > hd->blocksize) ? hd->blocksize : size);
                memcpy(mmap_head, buf, n);
                mmap_head += n;
                size -= n;
        }

        /* indirect block number */
        if (inode->i_block[12]) {
                disk_read(hd->device, inode->i_block[12] * hd->blocksize, (char *) p, hd->blocksize);

                for (i = 0; i < hd->blocksize / 4 && p[i]; i++) {
                        disk_read(hd->device, p[i] * hd->blocksize, buf, hd->blocksize);

                        n = ((size > hd->blocksize) ? hd->blocksize : size);
                        memcpy(mmap_head, buf, n);
                        mmap_head += n;
                        size -= n;
                }
        }

        /* bi-indirect block number */
        if (inode->i_block[13]) {
                disk_read(hd->device, inode->i_block[13] * hd->blocksize, (char *) p, hd->blocksize);

                for (i = 0; i < hd->blocksize / 4 && p[i]; i++) {
                        disk_read(hd->device, p[i] * hd->blocksize, (char *) pp, hd->blocksize);

                        for (j = 0; j < hd->blocksize / 4 && pp[j]; j++) {
                                disk_read(hd->device, pp[j] * hd->blocksize, buf, hd->blocksize);

                                n = ((size > hd->blocksize) ? hd-> blocksize : size);
                                memcpy(mmap_head, buf, n);
                                mmap_head += n;
                                size -= n;
                        }
                }
        }

        /* tri-indirect block number */
        if (inode->i_block[14]) {
                disk_read(hd->device, inode->i_block[14] * hd->blocksize, (char *) p, hd->blocksize);

                for (i = 0; i < hd->blocksize / 4 && p[i]; i++) {
                        disk_read(hd->device, p[i] * hd->blocksize, (char *) pp, hd->blocksize);

                        for (j = 0; j < hd->blocksize / 4 && pp[j]; j++) {
                                disk_read(hd->device, pp[j] * hd->blocksize, (char *) ppp, hd->blocksize);

                                for (k = 0; k < hd->blocksize / 4 && ppp[k]; k++) {
                                        disk_read(hd->device, ppp[k] * hd->blocksize, buf, hd->blocksize);

                                        n = ((size > hd->blocksize) ? hd->blocksize : size);
                                        memcpy(mmap_head, buf, n);
                                        mmap_head += n;
                                        size -= n;
                                }
                        }
                }
        }

        kfree(buf);
        kfree(p);
        kfree(pp);
        kfree(ppp);

        return mmap_base;
}

Le bloc de descripteurs de groupes

Le disque est subdivisé en groupes. L'organisation de chaque groupe est décrite par un descripteur qui fournit les informations suivantes :

  • Le numéro de bloc où se situe le block bitmap : bg_block_bitmap
  • Le numéro de bloc où se situe le inode bitmap : bg_inode_bitmap
  • Le numéro de bloc où se situe la table d'inodes : bg_inode_table

Notez que les offsets ci-dessus sont exprimés en numéro de bloc à partir du début du disque. L'offset en octet se calcule en multipliant ce numéro de bloc par la taille de bloc.
La fonction ext2_read_gd() crée un tableau contenant l'ensemble des descripteurs de groupes.

Localiser sur le disque une inode

Une inode contient les informations de base d'un fichier. Une inode est localisable sur le disque à partir des calculs suivants :

  • Localiser le groupe où se trouve l'inode : (i_num - 1) / s_inodes_per_group
  • Retrouver l'inode dans le groupe en calculant son index dans la table d'inodes : (i_num - 1) % s_inodes_per_group

La fonction ext2_read_inode() retrouve et renvoit une structure d'inode à partir de son numéro passé en argument.

Lire un fichier

La fonction ext2_read_file() renvoie un buffer avec le contenu du fichier correspondant à l'inode passée en argument.

Créer et utiliser une image d'un disque dur sous Unix avec un système de fichier Ext2FS

Attention ! Les commandes shell décrites ci-dessous doivent être parfaitement comprises avant d'être exécutées sur un système UNIX ! Si une commande ou un concept ne vous semble pas clair, je vous conseille de lire la documentation accessible sur l'excellent Guide du Rootard. Même si cet effort vous semble futile et n'a en apparence pas de rapport avec le développement d'un noyau, je vous garanti que les connaissances acquises vous seront très utiles !

Il faut d'abord créer une image d'un disque dur avec la commande bximage :

$ bximage
> hd
> flat
> 2
> c.img

Pour créer un système de fichiers :

$ mke2fs c.img
> y

Pour voir les caractéristiques du système :

$ dumpe2fs c.img

Pour monter le système de fichiers :

# mount -o loop -t ext2 c.img /mnt/loop

< Lire et écrire sur un dique IDE | TutoOS | Créer et lancer une application au format ELF à partir du système de fichier >

print · rss · source
Page last modified on November 07, 2010, at 01:19 PM