Introduction à Virtual File System

Exemples en C sous Linux 2.6.30


précédentsommairesuivant

V. Quelques informations

V-A. La segmentation en blocks pour les blocks device

Comme je l'ai laissé entendre, sans jamais développer le sujet, les données d'un fichier (ou répertoire) sont souvent fragmentées sur le disque. Cela dépend de la topologie du filesystem, mais il est bien souvent plus simple de prendre le premier block libre que l'on trouve que de déplacer d'importantes masses de données sous prétexte que l'on se refuse à fragmenter un fichier.

Dans le cas de minix, la partition est divisée comme ceci :

Image non disponible

Les deux premiers éléments dressent une liste des inodes disponibles sur la partition, puis des blocks disponibles (c'est à dire l'espace de la partition en octets divisé par la taille d'un block, elle aussi en octets. Le superblock est alors le premier block de cette liste).

Suit la table des inodes, qui est l'espace réservé aux inodes. Ainsi, le nombre d'inodes maximum sur une partition est défini lors du formatage de celle-ci.

Pour un système type ext2, on retrouvera une structure similaire, qui se répète à plusieurs reprises sur la partition, et dispose donc de copies du superblock en cas de corruption.

V-B. Lookup

Je n'ai fait que mentionner le nom de cette fonction, pourtant cruciale.

Lookup est la fonction qui permet de résoudre un path (chemin) vers un fichier/répertoire, et donc d'obtenir la dentry de cette élément.

Concrètement, la résolution de « /dir/subdir/file » produira les appels suivants :

  • dir_dentry = lookup(root_dentry, « dir »)
  • subdir_dentry = lookup(dir_dentry, « subdir »)
  • file_dentry = lookup(subdir_dentry, « file »)

Nous disposons de la dentry associée au point de montage grâce au super_block. Par la suite, on parcourt l'arbre (si bien sûr les nœuds existent).

Cette fonction est implémentée par le filesystem. Elle a un comportement similaire à readdir, puisqu'il lui faut lire le contenu d'un dossier pour obtenir l'inode du nœud suivant, et créer/récupérer la dentry associée.

V-C. Readdir

Cette fonction est chargée de lister le contenu d'un répertoire, en fournissant des paires (<nom de fichier>,<inode id>).

Dans un filesystem type minix, un dossier est simplement un fichier subdivisé en structure contenant ces paires. Ajouter un lien physique revient alors simplement à ajouter une structure pointant sur une inode existante. De même, supprimer un lien vers un fichier, c'est simplement supprimer cette structure (si l'inode du fichier considéré se retrouve avec un i_nlink de 0, c'est qu'elle doit être supprimée).

On comprend alors pourquoi le nom d'un fichier ne figure pas dans une inode, mais bien dans chaque dossier lié à cette inode.

Voici un exemple de ce à quoi peut ressembler cette fonction :

 
Sélectionnez
//Dans cette exemple, la taille d'une structure élémentaire représentant une paire n'est pas fixe.
static int
yfs_readdir
(struct file *file, void *dirent, filldir_t filldir)
{
  //Get page index
  unsigned long pidx   = file->f_pos >> PAGE_CACHE_SHIFT;
  //Get data offset
  unsigned long offset   = file->f_pos & ~PAGE_CACHE_MASK;
  //File's inode
  struct inode* inode   = file->f_dentry->d_inode;
  //Parent directory
  struct inode* parent   = file->f_dentry->d_parent->d_inode;
  //How much pages in this file?
  unsigned long cnt_pages   = yfs_count_pages(inode);
  //Page used
  struct page  *page  = NULL;
  //yfs directory entry
  struct yfs_dirent*  dent;
  //Page address
  char  *kaddr;
  //Len name
  int  len;
  //Page doesn't exist!
  if (file->f_pos > inode->i_size + IDX_ADD)
    goto done;
  //On each page
  for(;pidx < cnt_pages; pidx++)
    {
    //Get page or next page
    page = yfs_get_page(inode, pidx);
    if (IS_ERR(page))
      continue;
    //Lock page
    lock_page(page);
    //End is ino = 0 (An entry can't be broken into 2 pages!)
    kaddr = (void*)page_address(page);
    dent = (void*)kaddr + offset;
    while (dent->d_ino)
    {
      len = strlen(dent->d_name);
      offset = (char*)dent - kaddr;
      //Fill file (unknow type, fs will know by checking inode latter)
      if (filldir(dirent, dent->d_name, len,
      file->f_pos = ((pidx << PAGE_CACHE_SHIFT) | offset),
      dent->d_ino, DT_UNKNOWN))
      {
	//Unlock the page
	unlock_page(page);
	//filldir error
	yfs_put_page(page);
	goto out;
      }
      dent = yfs_next_dentry(dent, len);
    }
    //Unlock and put page
    unlock_page(page);
    yfs_put_page(page);
    offset = 0;
  }
  //. and .. are special directory
  if (offset == 0 && filldir(dirent, ".", 1, file->f_pos = ((pidx << PAGE_CACHE_SHIFT) | offset), inode->i_ino, DT_DIR))
    goto out;
  offset = 1;
  filldir(dirent, "..", 2,
  file->f_pos = ((pidx << PAGE_CACHE_SHIFT) | offset),
  parent->i_ino, DT_DIR);
  //Finish, . .. and others writed.
  done:
  file->f_pos = inode->i_size + IDX_ADD + 1;
  return 1;
  //Out, all dirent aren't already writed
  out:
return 0;
}

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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.