IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Introduction à Virtual File System

Exemples en C sous Linux 2.6.30


précédentsommairesuivant

III. Structures élémentaires

III-A. File System Type

fs.h
Sélectionnez
1738.
struct file_system_type {		//...

Cette structure décrit un filesystem pour qu'il puisse être enregistré dans la liste des FS disponibles.

Voici un exemple simple :

 
Sélectionnez
//FS type
static struct file_system_type yfs_fs_type =
{
  .name 	= "yfs",
  .get_sb 	= yfs_get_sb, 		//Get superblock (mount)
  .kill_sb 	= yfs_kill_sb, 		//Kill superblock (umount)
  .owner 	= THIS_MODULE, 		//The filesystem owner(this module)
  .fs_flags 	= FS_REQUIRES_DEV, 	//Tell we need a block device
};

III-B. Inode

fs.h
Sélectionnez
719.
struct inode {		//...

La structure inode contient toutes les informations relatives à l'état d'un fichier(3). Voici le détail des champs les plus usuels.

i_ino : numéro identificateur propre à chaque inode. Pour certains systèmes de fichiers qui n'ont pas d'inode fixe (FAT32) ce numéro est généré dynamiquement par exemple par iunique. Pour des systèmes de fichiers comme ext2/3, ce numéro est propre à chaque fichier, les ids 0 et 1 ne sont pas utilisables, la racine (/) se voyant alors attribuer l'inode numéro 2. (les valeurs 0 et 1 sont respectivement, une valeur invalide et une inode utilisée pour lier tous les blocks considérés comme défectueux du disque).

i_count : nombre de contextes d'utilisation de l'inode. Incrémenté à chaque ouverture d'un fichier, et décrémenté lors de sa fermeture.

i_mode : il s'agit du chmod du fichier/répertoire ainsi que du type de fichier. Ce peut être un fichier, un répertoire, un lien dur, un lien symbolique, un char device, un block device, un point de montage... Généralement on se contente de gérer les cas qui relèvent du filesystem (Lien, fichier et répertoire).
La liste des valeurs est disponible dans include/linux/stat.h

i_nlink : nombre de liens physiques pointant sur la même inode. Dans le cas d'un système de fichiers comme ext3, c'est cette valeur qui permet de savoir lors de la suppression d'un fichier si l'inode et le contenu associé doivent être détruits ou bien conservés pour un autre fichier.
Un lien physique (ln src_file link_name) incrémente cette valeur, et la suppression d'un des liens (Il n'y a pas « d'original » ou de « premier fichier », toutes les entrées vers ce fichier/cette inode se valent) entrainera la décrémentation de cette valeur. Quand i_nlink atteint 0, le système sait que le fichier peut être considéré comme « détruit » et les données libérées (c'est le filesystem qui se chargera de cette tâche).

i_uid && i_gid : ce sont respectivement l'identifiant de l'utilisateur possédant le fichier et le groupe auquel appartient le fichier. La correspondance peut parfois être faite en observant les fichiers /etc/passwd et /etc/group.

i_size : la taille (en octets) du fichier/répertoire considéré. Par exemple, les répertoires ont "souvent" une taille multiple de 4096 octets (correspondant à une page mémoire sous certaines architectures)
Il s'agit bien de la taille des données contenues dans le fichier, et non de l'espace utilisé sur le périphérique de stockage.

i_atime && i_mtime && i_ctime, : ce sont respectivement les derniers timestamp :

  • du dernier accès à un fichier (lecture)
  • de la dernière modification d'un fichier
  • du dernier changement de statut du fichier (par exemple, chmod)

i_blocks : le nombre de blocs utilisés pour stocker les données du fichier/répertoire associé à l'inode. Notez toutefois qu'un filesystem peut fonctionner en se passant de cette valeur. Maintenir sa valeur à jour rend les informations fournies par stat et d'autres commandes plus exactes.

i_op : un lien vers la structure « inode operations » liée à ce fichier. (cf: inode operations)

i_fop : un lien vers la structure « file operations » liée à ce fichier. (cf: file operations)

i_aops : un lien vers la structure « address_space_operations » liée à ce fichier. (cf: address_space_operations)

(3) A noter qu'il sera parfois nécessaire d'adjoindre des données supplémentaires à une inode, par exemple la place des premiers blocs du fichier correspondant. On créera alors une superstructure qui contient les données et l'inode, et on utilisera les macros fournies par le kernel comme container_of. Ceci sera abordé à nouveau plus tard dans ce document.

III-C. Superblock

fs.h
Sélectionnez
1316.
struct super_block {		//...

Cette structure que nous avons déjà évoquée contient les informations cruciales du filesystem monté.
Toutefois, il n'y a que deux éléments essentiels que vous devez impérativement définir vous même lors de l'appel de fill_super :

s_op : un lien vers la structure « super opérations » liée au superblock. Cette structure est cruciale puisque c'est elle qui fournira les fonctions élémentaires qui permettent de parcourir l'arborescence. (cf: super opérations)

s_root : la dentry liée à l'inode décrivant le répertoire / (dentry pouvant être allouée avec int d_alloc_root(struct *inode) une fois la structure inode de / construite par vos soins).

III-D. Super Operations

fs.h
Sélectionnez
1555.
struct super_operations  {		//...

Cette structure est capitale. Elle contient les pointeurs sur les fonctions qui permettront la dés-allocation du superblock, l'écriture d'une inode sur le disque, sa suppression, sa libération, la création d'une inode, ou encore remonter le filesystem. Les noms des champs sont on ne peut plus explicites.

Voici les fonctions essentielles, la liste complète et documentée figure dans vfs.txt fourni dans la documentation du kernel.

 
Sélectionnez
//Super operations
static struct super_operations yourfs_super_operations =
{
  .alloc_inode 		= yfs_alloc_inode, 	//Allocate inode's memory
  .destroy_inode 	= yfs_destroy_inode, 	//Free inode's memory
  .write_inode 		= yfs_write_inode, 	//Write inode's data on device
  .delete_inode 	= yfs_delete_inode, 	//Inode Destruction
  .put_super 		= yfs_put_super, 	//Free superblock
  .statfs 		= simple_statfs 	/* simple statfs */,
  .remount_fs 		= NULL, 		//Remount the filesystem
};

III-E. Inode Operations

Cette structure fournit les pointeurs de fonctions qui permettent d'agir sur le fichier/répertoire. On distinguera donc le cas d'un fichier contenant simplement des données d'un répertoire contenant une liste d'inodes nommées.

Voici deux exemples simples pour ces deux cas. Vous trouverez aussi un exemple pour les liens symboliques. N'hésitez pas à consulter la documentation et les headers des structures pour avoir la liste complète des champs disponibles.

 
Sélectionnez
//Repertoires
struct inode_operations yfs_dir_iops =
{
  //Permet de resoudre un chemin
  .lookup 	= yfs_lookup,
  //Crée un fichier, peut se contenter d'appeler mknod
  .create 	= yfs_create,
  //Crée un noeud (fichier, répertoire, char/block device, ...)
  .mknod 	= yfs_mknod,
  //Crée un repertoire, peut se contenter d'adapter i_nlink et appeler mknod
  .mkdir 	= yfs_mkdir,
  //Supprime un repertoire du filesystem
  .rmdir 	= yfs_rmdir,
  //Lien "dure"
  .link 	= yfs_link,
  //Supprime un fichier
  .unlink 	= yfs_unlink,
  //Crée un lien symbolique. On pourra utiliser page_symlink
  .symlink 	= yfs_symlink,
};
 
Sélectionnez
//Fichiers
struct inode_operations yfs_file_iops =
{
  //Libère les bloques de l'inode suite à un redimensionnement
  .truncate 	= sfs_truncate,
  //Permet de récupérer les attributs du fichier
  .getattr 	= yfs_getattr,
};
 
Sélectionnez
//Liens symbolique
struct inode_operations yfs_symlink_iops =
{
  .readlink 	= generic_readlink,
  .follow_link 	= page_follow_link_light,
  .put_link 	= page_put_link,
  .getattr 	= yfs_getattr,
};

III-F. File Operations

A nouveau, les opérations qui peuvent être effectuées sur un nœud dépendent de l'inode considérée. De plus, vous pouvez choisir de rendre la lecture d'un répertoire comme d'un fichier possible ou impossible, etc...

Voici quelques exemples minimaux :

 
Sélectionnez
struct file_operations yfs_file_ops =
{
  .llseek 	= generic_file_llseek,
  .read 	= do_sync_read,
  .aio_read 	= generic_file_aio_read,
  .write 	= do_sync_write,
  .aio_write 	= generic_file_aio_write,
  .mmap 	= generic_file_mmap,
  .splice_read 	= generic_file_splice_read,
};
 
Sélectionnez
struct file_operations yfs_dir_ops =
{
  .read 	= generic_read_dir,
  .readdir 	= yfs_readdir,
};

On constate que le kernel fournit de quoi assurer nombre d'opérations élémentaires. Ceci sous réserve de fournir une structure dans le champ a_ops valide.

III-G. Adresse Space Operations

La structure « Address Space Operations » permet de faire abstraction de la discontinuité des données enregistrées sur le périphérique et de les représenter comme une succession de pages mémoire connexe. La compréhension de cet outil est essentielle.
Dans l'exemple suivant, nous présenterons une utilisation simple où nous nous contenterons de renvoyer le numéro du block contenant la Nième page mémoire d'un fichier. Ceci n'est qu'un cas particulier, et vous retrouverez cette structure dans les filesystem virtuels, qui ne requièrent pas de périphérique.

 
Sélectionnez
//Maping virtual connex memory of a file into physical blocks
struct address_space_operations sfs_address_space_ops =
{
  .readpage 	= yfs_readpage,
  .writepage 	= yfs_writepage,
  .sync_page 	= block_sync_page,
  .write_begin 	= yfs_write_begin,
  .write_end 	= generic_write_end,
  .bmap 	= sfs_bmap,
};
 
Sélectionnez
//Read page with yfs_get_block
static int yfs_readpage
(struct file *file, struct page *page)
{
  printk(KERN_DEBUG "yfs_readpage\n");
  return block_read_full_page(page, yfs_get_block);
}
//Write page with yfs_get_block
static int yfs_writepage
(struct page *page, struct writeback_control *wbc)
{
  printk(KERN_DEBUG "yfs_writepage\n");
  return block_write_full_page(page, yfs_get_block, wbc);
}
//Prepare Write page
int __yfs_write_begin
(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
  printk(KERN_DEBUG "__yfs_write_begin\n");
  //Called by us, so pagep is initialised
  return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, yfs_get_block);
}
//Prepare Write page with yfs_get_block
static int yfs_write_begin
(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
  printk(KERN_DEBUG "yfs_write_begin\n");
  //Called by kernel, *pagep is uninitialised!
  *pagep = NULL;
  return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, yfs_get_block);
}

La fonction yfs_get_block associe une partie d'un fichier au block où se situent les données de cette partie. Voici un aperçu de ce à quoi peut ressembler cette fonction :

 
Sélectionnez
//Associate logical block to physical block
int yfs_get_block
(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
{
  //...
  int
  err = -EIO;
  //Invalid negativ block number!
  if (iblock < 0)
  {
    printk("YFS-warning: In get_block, invalid iblock = %u\n", (unsigned int)iblock);
    return err;
  }
  //Search the grate block from inode's data and store result in blk
  if((err = yfs_find_block(inode, /* ... */ &blk)) < 0)
    goto err;
  //Block found
  if (blk)
    goto map;
  //Get a new block and mark it used in fs block's list.
  if((err = yfs_alloc_block(inode, /* .. */ &blk) < 0))
    goto err;
  //Map block into bh
  map:
  map_bh(bh_result, inode->i_sb, blk);
  return 0;
  err:
  return err;
}

Ici, tout le travail est relégué à yfs_find_block et yfs_alloc_block.


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2010 Jérémy Cochoy. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.