View of xos/fs/vfat_instance.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "vfat_instance_struct.h"
#include "vfat_traverse.h"
#include "vfat_iter_struct.h"
#include "vfat_attr_struct.h"
#include "vfat_names.h"
#include "vfat_collide.h"
#include "vfat_cluster_set.h"
#include "vfat_lock.h"
#include "vfat_fat_info.h"
#include "device_struct.h"
#include "buffer.h"
 
#include <stat_struct.h>
#include <verify_area.h>
#include <segment.h>
#include <time.h>
#include <mutex.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <config.h>
#include <misc.h>
#include <util.h>
#include <string.h>
#include <limits.h>
 
#define MAJOR_REVISION_NUMBER 0
#define MINOR_REVISION_NUMBER 0
 
/* Utilitaires */
 
static inline unsigned int wstrlen(const unsigned short *str)
{
  unsigned int len;
 
  len = 0;
  while (*str++)
    len++;
  return len;
}
 
static inline unsigned int get_word_size(int fat_type)
{
  switch (fat_type) {
  case FAT12: /* 12 bits */
  case FAT16: /* 16 bits */
    return 2;
  case FAT32: /* 32 bits */
  default:
    return 4;
  }
}
 
/* La valeur de la marque EOC est dependante du systeme d'exploitation. Elle
 * doit verifier IsEOF(). */
static inline unsigned long get_eoc_mark(int fat_type)
{
  switch (fat_type) {
  case FAT12:
    return 0x0fff;
  case FAT16:
    return 0xffff;
  case FAT32:
  default:
    return 0x0fffffff;
  }
}
 
static inline unsigned long get_bad_cluster_mark(int fat_type)
{
  switch (fat_type) {
  case FAT12:
    return 0x0ff7;
  case FAT16:
    return 0xfff7;
  case FAT32:
  default:
    return 0x0ffffff7;
  }
}
 
static inline int datecmp(const struct calendar_time_struct *calendar_time1, const struct calendar_time_struct *calendar_time2)
{
  if (calendar_time1->year != calendar_time2->year)
    return calendar_time1->year < calendar_time2->year ? -1 : 1;
  if (calendar_time1->mon != calendar_time2->mon)
    return calendar_time1->mon < calendar_time2->mon ? -1 : 1;
  if (calendar_time1->mday != calendar_time2->mday)
    return calendar_time1->mday < calendar_time2->mday ? -1 : 1;
  return 0;
}
 
/* vfat_instance */
 
struct vfat_instance_collide_fo_struct {
  struct vfat_collide_fo_struct collide_fo;
  struct vfat_instance_struct *vfat_instance;
  unsigned long dir_ent_key;
};
 
static void init_vfat_instance_collide_fo(struct vfat_instance_collide_fo_struct *vfat_instance_collide_fo, struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key);
 
static void set_io_error(struct vfat_instance_struct *vfat_instance);
 
/* Utilitaires sur vfat_instance */
 
static inline int check_tot_sec(const struct vfat_instance_struct *vfat_instance)
{
  /* Le pilote est donc actuellement limite aux volumes de 128 Go.
     Voir la section dir_ent_key pour la raison de cette limitation. */
  return vfat_instance->fat_info.TotSec <= exp2(37 - vfat_instance->log_sec_size);
}
 
static inline int is_valid_data_cluster_number(const struct vfat_instance_struct *vfat_instance, unsigned long n)
{
  return n >= 2 && n <= vfat_instance->fat_info.CountofClusters + 1;
}
 
/* Le calcul est fait de facon a eviter les overflows. */
static inline unsigned long count_clusters(const struct vfat_instance_struct *vfat_instance, unsigned long size)
{
  return (size >> vfat_instance->log_clus_size) + (((size & (vfat_instance->clus_size - 1)) + vfat_instance->clus_size - 1) >> vfat_instance->log_clus_size);
}
 
/* sec_num doit etre un numero de secteur de donnees de sorte que cette
 * operation ait un sens. */
static inline unsigned long get_cluster_of_sector(const struct vfat_instance_struct *vfat_instance, unsigned long sec_num)
{
  return ((sec_num - vfat_instance->fat_info.FirstDataSector) / vfat_instance->fat_info.boot_sect.BPB_SecPerClus) + 2;
}
 
/* load/write/sync */
 
/* Charge des donnees du disque en memoire.
 * Retourne 0 en cas d'echec. */
static int load_data(struct vfat_instance_struct *vfat_instance, unsigned long block, unsigned long offset, void *buffer, unsigned long size)
{
  void *buf;
  unsigned long len;
 
  while (size > 0) {
    len = min(size, BLOCK_SIZE - offset);
    if (!(buf = buffer_get(vfat_instance->device, block)))
      goto error;
    if (!buffer_update(buf))
      goto error_release;
    memcpy(buffer, buf + offset, len);
    buffer_release(buf);
    block++;
    offset = (offset + len) & (BLOCK_SIZE - 1);
    buffer += len;
    size -= len;
  }
  return 1;
 
 error_release:
  buffer_release(buf);
 error:
  set_io_error(vfat_instance);
  return 0;
}
 
/* Ecrit des donnees en memoire sur le disque.
 * Retourne 0 en cas d'echec. */
static int write_data(struct vfat_instance_struct *vfat_instance, unsigned long block, unsigned long offset, const void *buffer, unsigned long size)
{
  void *buf;
  unsigned long len;
 
  while (size > 0) {
    len = min(size, BLOCK_SIZE - offset);
    if (!(buf = buffer_get(vfat_instance->device, block)))
      goto error;
    if (!buffer_update(buf))
      goto error_release;
    memcpy(buf + offset, buffer, len);
    buffer_dirty(buf);
    buffer_release(buf);
    block++;
    offset = (offset + len) & (BLOCK_SIZE - 1);
    buffer += len;
    size -= len;
  }
  return 1;
 
 error_release:
  buffer_release(buf);
 error:
  set_io_error(vfat_instance);
  return 0;
}
 
/* sync() n'a volontairement pas de valeur de retour, car ce n'est qu'un choix
 * d'implementation de l'appeler au retour des appels au systeme de fichier
 * (recopie immediate). */
static void sync(struct vfat_instance_struct *vfat_instance)
{
  if (!buffer_sync(vfat_instance->device))
    set_io_error(vfat_instance);
}
 
/* get/set_fat_entry */
 
static int get_this_fat_entry(struct vfat_instance_struct *vfat_instance, unsigned int i, unsigned long n, unsigned long *val)
{
  unsigned long sec_num;
  unsigned long ent_offset;
  unsigned long blk_num;
  unsigned long blk_offset;
  unsigned char buf[4];
 
  sec_num = ThisFATSecNum(&vfat_instance->fat_info, n) + i * vfat_instance->fat_info.FATSz;
  ent_offset = ThisFATEntOffset(&vfat_instance->fat_info, n);
  if (vfat_instance->log_sec_size > vfat_instance->log_blk_size) {
    blk_num = (sec_num << (vfat_instance->log_sec_size - vfat_instance->log_blk_size)) + (ent_offset >> vfat_instance->log_blk_size);
    blk_offset = ent_offset & (BLOCK_SIZE - 1);
  }
  else {
    blk_num = sec_num >> (vfat_instance->log_blk_size - vfat_instance->log_sec_size);
    blk_offset = ((sec_num & (BLOCK_SIZE - 1)) * (vfat_instance->fat_info.boot_sect.BPB_BytsPerSec & (BLOCK_SIZE - 1)) + ent_offset) & (BLOCK_SIZE - 1);
  }
  if (!load_data(vfat_instance, blk_num, blk_offset, buf, get_word_size(vfat_instance->fat_info.FATType)))
    return 0;
  *val = get_FATClusEntryVal(&buf[-ent_offset], &vfat_instance->fat_info, n);
  if (!*val && n != get_bad_cluster_mark(FAT32))
    vfat_cluster_set_add(&vfat_instance->free_cluster_set, n);
  return 1;
}
 
static int set_this_fat_entry(struct vfat_instance_struct *vfat_instance, unsigned int i, unsigned long n, unsigned long val)
{
  unsigned long sec_num;
  unsigned long ent_offset;
  unsigned long blk_num;
  unsigned long blk_offset;
  unsigned char buf[4];
 
  sec_num = ThisFATSecNum(&vfat_instance->fat_info, n) + i * vfat_instance->fat_info.FATSz;
  ent_offset = ThisFATEntOffset(&vfat_instance->fat_info, n);
  if (vfat_instance->log_sec_size > vfat_instance->log_blk_size) {
    blk_num = (sec_num << (vfat_instance->log_sec_size - vfat_instance->log_blk_size)) + (ent_offset >> vfat_instance->log_blk_size);
    blk_offset = ent_offset & (BLOCK_SIZE - 1);
  }
  else {
    blk_num = sec_num >> (vfat_instance->log_blk_size - vfat_instance->log_sec_size);
    blk_offset = ((sec_num & (BLOCK_SIZE - 1)) * (vfat_instance->fat_info.boot_sect.BPB_BytsPerSec & (BLOCK_SIZE - 1)) + ent_offset) & (BLOCK_SIZE - 1);
  }
  if (!load_data(vfat_instance, blk_num, blk_offset, buf, get_word_size(vfat_instance->fat_info.FATType)))
    return 0;
  set_FATClusEntryVal(&buf[-ent_offset], &vfat_instance->fat_info, n, val);
  if (!write_data(vfat_instance, blk_num, blk_offset, buf, get_word_size(vfat_instance->fat_info.FATType)))
    return 0;
  return 1;
}
 
/* n doit etre inferieur ou egal a vfat_instance->fat_info.CountofClusters + 1.
 * Retourne 0 en cas d'echec. */
static int get_fat_entry(struct vfat_instance_struct *vfat_instance, unsigned long n, unsigned long *val)
{
  unsigned int i;
 
  if (!vfat_instance->mirror)
    return get_this_fat_entry(vfat_instance, vfat_instance->active_fat, n, val);
  for (i = 0; i < vfat_instance->fat_info.boot_sect.BPB_NumFATs; i++)
    if (get_this_fat_entry(vfat_instance, i, n, val))
      return 1;
  return 0;
}
 
/* n doit etre inferieur ou egal a vfat_instance->fat_info.CountofClusters + 1.
 * Retourne 0 en cas d'echec. */
static int set_fat_entry(struct vfat_instance_struct *vfat_instance, unsigned long n, unsigned long val)
{
  int st;
  unsigned int i;
 
  if (!vfat_instance->mirror)
    st = set_this_fat_entry(vfat_instance, vfat_instance->active_fat, n, val);
  else {
    st = 1;
    for (i = 0; i < vfat_instance->fat_info.boot_sect.BPB_NumFATs; i++)
      st &= set_this_fat_entry(vfat_instance, i, n, val);
  }
  if (st) {
    if (val || n == get_bad_cluster_mark(FAT32))
      vfat_cluster_set_del(&vfat_instance->free_cluster_set, n);
    else
      vfat_cluster_set_add(&vfat_instance->free_cluster_set, n);
  }
  return st;
}
 
/* Gestion des clusters libres */
 
/* Retourne 0 en cas d'echec. */
static unsigned long get_free_cluster(struct vfat_instance_struct *vfat_instance)
{
  unsigned long n;
  unsigned int i;
  unsigned long val;
 
  /* discrete optimisation destinee a favoriser les sequences de clusters libres consecutifs */
  if (vfat_instance->last_free <= vfat_instance->fat_info.CountofClusters && get_fat_entry(vfat_instance, vfat_instance->last_free + 1, &val) && !val) {
    n = vfat_instance->last_free + 1;
    goto success;
  }
 
  if (!(n = vfat_cluster_set_get(&vfat_instance->free_cluster_set))) {
    for (i = 0; i < vfat_instance->fat_info.CountofClusters; i++) {
      if (vfat_instance->next_free == get_bad_cluster_mark(FAT32)) /* marque "BAD CLUSTER" en FAT32 */
        goto next;
      if (!get_fat_entry(vfat_instance, vfat_instance->next_free, &val))
        goto failure;
      if (!val) {
        n = vfat_instance->next_free;
        goto success;
      }
    next:
      if (++vfat_instance->next_free == vfat_instance->fat_info.CountofClusters + 2)
        vfat_instance->next_free = 2;
    }
    goto failure;
  }
 success:
  vfat_instance->last_free = n;
  return n;
 
 failure:
  return 0;
}
 
/* Alloue un cluster comme suivant de previous_cluster.
 * Le cluster alloue est marque comme dernier de la chaine de clusters.
 * Si *previous_cluster est nul, il est considere comme non alloue. Le nouveau
 * cluster lui est alors assigne.
 * Si *previous_cluster n'est pas nul, il doit etre marque comme fin de chaine
 * de clusters.
 * La coherence du systeme de fichiers peut etre brisee en cas d'erreur.
 * Retourne le nouveau cluster en cas de succes, 0 en cas d'echec. */
static unsigned long alloc_cluster(struct vfat_instance_struct *vfat_instance, unsigned long *previous_cluster)
{
  unsigned long cluster;
  unsigned long retval;
 
  if (!mutex_lock(&vfat_instance->alloc_mutex))
    return 0;
  if (!(cluster = get_free_cluster(vfat_instance))) {
    retval = 0;
    goto unlock;
  }
  if (!set_fat_entry(vfat_instance, cluster, get_eoc_mark(vfat_instance->fat_info.FATType))) {
    retval = 0;
    goto unlock;
  }
  if (*previous_cluster) {
    if (!set_fat_entry(vfat_instance, *previous_cluster, cluster)) {
      retval = 0;
      goto unlock;
    }
  }
  else
    *previous_cluster = cluster;
  retval = cluster;
 unlock:
  mutex_unlock(&vfat_instance->alloc_mutex);
  return retval;
}
 
/* Alloue et ajoute des clusters a la chaine de clusters commencant par
 * first_cluster jusqu'a ce qu'elle contienne au moins count clusters. Le
 * nombre total de clusters, dans la limite de count clusters, est retourne
 * dans tot_count. Il peut etre inferieur a celui demande s'il n'y a pas assez
 * d'espace sur le volume.
 * Retourne un code d'erreur en cas d'echec. */
static int increase_clusterchain(struct vfat_instance_struct *vfat_instance, unsigned long *first_cluster, unsigned long count, unsigned long *tot_count)
{
  unsigned long cluster, next_cluster;
 
  /* parcours de la chaine de clusters */
  cluster = 0;
  next_cluster = *first_cluster;
  *tot_count = 0;
  if (next_cluster)
    do {
      if (!count--)
        return 0;
      (*tot_count)++;
      cluster = next_cluster;
      if (!get_fat_entry(vfat_instance, cluster, &next_cluster))
        return -EIO;
      if (IsEOF(&vfat_instance->fat_info, next_cluster))
        break;
      if (!is_valid_data_cluster_number(vfat_instance, next_cluster))
        return -EINVAL;
    }
    while (1);
 
  /* allocation et ajout des nouveaux clusters */
  while (count-- && (next_cluster = alloc_cluster(vfat_instance, cluster ? &cluster : first_cluster))) {
    cluster = next_cluster;
    (*tot_count)++;
  }
 
  return 0;
}
 
/* Libere la chaine de clusters commencant par cluster.
 * cluster doit etre lui-meme alloue.
 * Retourne un code d'erreur en cas d'echec. */
static int free_clusterchain(struct vfat_instance_struct *vfat_instance, unsigned long cluster)
{
  int retval;
  unsigned long next_cluster;
 
  if (!mutex_lock(&vfat_instance->alloc_mutex))
    return -EIO;
  do {
    if (!get_fat_entry(vfat_instance, cluster, &next_cluster)) {
      retval = -EIO;
      goto unlock;
    }
    if (!set_fat_entry(vfat_instance, cluster, 0)) {
      retval = -EIO;
      goto unlock;
    }
    if (IsEOF(&vfat_instance->fat_info, next_cluster))
      break;
    if (!is_valid_data_cluster_number(vfat_instance, next_cluster))
      return -EINVAL;
    cluster = next_cluster;
  }
  while (1);
  retval = 0;
 unlock:
  mutex_unlock(&vfat_instance->alloc_mutex);
  return retval;
}
 
/* set_io_error */
 
static void set_io_error(struct vfat_instance_struct *vfat_instance)
{
  unsigned long val;
 
  if (vfat_instance->read_only || vfat_instance->error)
    return;
  vfat_instance->error = 1;
 
  if (vfat_instance->fat_info.FATType != FAT12) {
    if (get_fat_entry(vfat_instance, 1, &val)) {
      switch (vfat_instance->fat_info.FATType) {
      case FAT16:
        val &= ~0x4000; /* disk I/O error */
        break;
      case FAT32:
        val &= ~0x04000000; /* disk I/O error */
        break;
      }
      set_fat_entry(vfat_instance, 1, val);
    }
  }
 
  vfat_instance->error = 0;
}
 
/* dir_ent_key */
 
/* Une cle d'entree de repertoire est un entier de 32 bits qui represente, de
 * facon equivalente :
 * - son offset sur le disque, divise par 32 ;
 * - son numero de bloc sur (32 - ulog2(blk_size / 32)) bits, suivi de son
 *   offset dans le bloc divise par 32 sur ulog2(blk_size / 32) bits ;
 * - son numero de secteur sur (32 - ulog2(sec_size / 32)) bits, suivi de son
 *   indice dans le secteur sur ulog2(sec_size / 32) bits.
 * Le diviseur 32 est la taille d'une entree de repertoire.
 * Une cle d'entree de repertoire valide n'est jamais nulle.
 * Une cle d'entree de repertoire etant limitee a 32 bits, la taille du volume
 * FAT est limitee a 4G * 32 = 128 Go. */
 
static inline unsigned long get_first_dir_ent_key(const struct vfat_instance_struct *vfat_instance, unsigned long sec_num)
{
  return sec_num << (vfat_instance->log_sec_size - 5);
}
 
static inline unsigned long get_dir_ent_key_blk_num(const struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key)
{
  return dir_ent_key >> (vfat_instance->log_blk_size - 5);
}
 
static inline unsigned long get_dir_ent_key_blk_offset(const struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key)
{
  return (dir_ent_key & ((BLOCK_SIZE >> 5) - 1)) << 5;
}
 
static inline unsigned long get_dir_ent_key_sec_num(const struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key)
{
  return dir_ent_key >> (vfat_instance->log_sec_size - 5);
}
 
static inline unsigned long get_dir_ent_key_sec_idx(const struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key)
{
  return dir_ent_key & ((vfat_instance->fat_info.boot_sect.BPB_BytsPerSec >> 5) - 1);
}
 
/* file_blk_key */
 
/* Un bloc de fichier (a ne pas confondre avec le bloc, unite d'entree/sortie)
 * est le plus gros morceau de fichier lisible en une seule entree/sortie. Il
 * correspond au plus petit entre secteur et bloc d'E/S.
 * Une cle de bloc de fichier est un entier 32 bits qui represente le numero du
 * bloc depuis le premier bloc du premier cluster de donnees, pris comme
 * origine. */
 
static inline unsigned long get_file_blk_key(const struct vfat_instance_struct *vfat_instance, unsigned long cluster, unsigned long cluster_offset)
{
  if (vfat_instance->log_sec_size <= vfat_instance->log_blk_size)
    return (cluster - 2) * vfat_instance->fat_info.boot_sect.BPB_SecPerClus + (cluster_offset >> vfat_instance->log_sec_size);
  else
    return ((cluster - 2) * vfat_instance->fat_info.boot_sect.BPB_SecPerClus) << (vfat_instance->log_sec_size - vfat_instance->log_blk_size);
}
 
static inline unsigned long get_file_blk_key_cluster(const struct vfat_instance_struct *vfat_instance, unsigned long file_blk_key)
{
  if (vfat_instance->log_sec_size <= vfat_instance->log_blk_size)
    return file_blk_key / vfat_instance->fat_info.boot_sect.BPB_SecPerClus + 2;
  else
    return file_blk_key / (vfat_instance->fat_info.boot_sect.BPB_SecPerClus << (vfat_instance->log_sec_size - vfat_instance->log_blk_size)) + 2;
}
 
static inline unsigned long get_file_blk_key_clus_offset(const struct vfat_instance_struct *vfat_instance, unsigned long file_blk_key)
{
  if (vfat_instance->log_sec_size <= vfat_instance->log_blk_size)
    return (file_blk_key & (vfat_instance->fat_info.boot_sect.BPB_SecPerClus - 1)) << vfat_instance->log_sec_size;
  else
    return (file_blk_key & ((vfat_instance->fat_info.boot_sect.BPB_SecPerClus << (vfat_instance->log_sec_size - vfat_instance->log_blk_size)) - 1)) << vfat_instance->log_blk_size;
}
 
static inline unsigned long get_file_blk_key_blk_num(const struct vfat_instance_struct *vfat_instance, unsigned long file_blk_key)
{
  if (vfat_instance->log_sec_size <= vfat_instance->log_blk_size)
    return (vfat_instance->fat_info.FirstDataSector + file_blk_key) >> (vfat_instance->log_blk_size - vfat_instance->log_sec_size);
  else
    return (vfat_instance->fat_info.FirstDataSector << (vfat_instance->log_sec_size - vfat_instance->log_blk_size)) + file_blk_key;
}
 
static inline unsigned long get_file_blk_key_blk_offset(const struct vfat_instance_struct *vfat_instance, unsigned long file_blk_key)
{
  if (vfat_instance->log_sec_size <= vfat_instance->log_blk_size)
    return ((vfat_instance->fat_info.FirstDataSector + file_blk_key) & ((1 << (vfat_instance->log_blk_size - vfat_instance->log_sec_size)) - 1)) << vfat_instance->log_sec_size;
  else
    return 0;
}
 
/* skip */
 
/* Avance de count file_blks dans la chaine de clusters ou evolue file_blk_key. */
static int skip(struct vfat_instance_struct *vfat_instance, unsigned long *file_blk_key, unsigned long count)
{
  const unsigned long file_blk_per_clus = vfat_instance->clus_size / vfat_instance->file_blk_size;
 
  unsigned long cluster;
  unsigned long clus_idx;
 
  cluster = get_file_blk_key_cluster(vfat_instance, *file_blk_key);
  clus_idx = get_file_blk_key_clus_offset(vfat_instance, *file_blk_key) / vfat_instance->file_blk_size;
 
  /* premier cluster */
  if (clus_idx) {
    if (count < file_blk_per_clus - clus_idx) {
      clus_idx += count;
      goto end;
    }
    count -= file_blk_per_clus - clus_idx;
    if (!get_fat_entry(vfat_instance, cluster, &cluster))
      return -EIO;
    if (IsEOF(&vfat_instance->fat_info, cluster)) /* end of clusterchain */
      return -EINVAL;
    if (!is_valid_data_cluster_number(vfat_instance, cluster))
      return -EINVAL;
  }
 
  /* clusters entiers */
  while (count >= file_blk_per_clus) {
    count -= file_blk_per_clus;
    if (!get_fat_entry(vfat_instance, cluster, &cluster))
      return -EIO;
    if (IsEOF(&vfat_instance->fat_info, cluster)) /* end of clusterchain */
      return -EINVAL;
    if (!is_valid_data_cluster_number(vfat_instance, cluster))
      return -EINVAL;
  }
 
  /* dernier cluster */
  clus_idx = count;
 
 end:
  *file_blk_key = get_file_blk_key(vfat_instance, cluster, clus_idx * vfat_instance->file_blk_size);
  return 0;
}
 
/* Divers */
 
static inline int check_first_cluster(const struct vfat_instance_struct *vfat_instance, unsigned long first_cluster)
{
  return !first_cluster || is_valid_data_cluster_number(vfat_instance, first_cluster);
}
 
static inline unsigned long get_root_first_sec_num(const struct vfat_instance_struct *vfat_instance)
{
  if (vfat_instance->fat_info.FATType == FAT32)
    return FirstSectorofCluster(&vfat_instance->fat_info, vfat_instance->fat_info.boot_sect.fat32.BPB_RootClus);
  else
    return vfat_instance->fat_info.FirstRootDirSecNum;
}
 
static inline unsigned long get_first_sec_num(const struct vfat_instance_struct *vfat_instance, unsigned long dir_id, unsigned long first_cluster)
{
  return !first_cluster ? vfat_instance->fat_info.FirstRootDirSecNum : FirstSectorofCluster(&vfat_instance->fat_info, first_cluster);
}
 
static int generate_names(struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key, const char *fs_basename, unsigned short longname[], unsigned char shortname[])
{
  struct vfat_instance_collide_fo_struct vfat_instance_collide_fo;
 
  init_vfat_instance_collide_fo(&vfat_instance_collide_fo, vfat_instance, dir_ent_key);
  return vfat_generate_names(fs_basename, longname, shortname, &vfat_instance_collide_fo.collide_fo);
}
 
static unsigned int count_dir_entries(struct vfat_instance_struct *vfat_instance, const unsigned short longname[], const unsigned char shortname[])
{
#if VFAT_USE_LONG_NAMES != 0
  unsigned int count;
 
  count = 1;
  if (!vfat_fit(longname, shortname))
    count += (wstrlen(longname) + 12) / 13;
  return count;
#else
  return 1;
#endif
}
 
/* Cree un repertoire vide. */
static int create_dir(struct vfat_instance_struct *vfat_instance, unsigned long *first_cluster)
{
  int rv;
  unsigned long tot_count;
 
  /* allocation d'un cluster */
  *first_cluster = 0;
  if ((rv = increase_clusterchain(vfat_instance, first_cluster, 1, &tot_count)) < 0)
    return rv;
  if (tot_count == 0) /* pas assez de place sur le disque */
    return -ENOSPC;
  /* initialisation du cluster */
  if ((rv = vfat_traverse_clear_dir(vfat_instance, get_first_dir_ent_key(vfat_instance, FirstSectorofCluster(&vfat_instance->fat_info, *first_cluster)))) < 0)
    return rv;
  return 0;
}
 
/* Agrandit un fichier.
 * *first_cluster est le premier cluster du fichier (nul si le fichier n'a pas
 * de clusters alloues).
 * *file_size est la taille du fichier.
 * size est la taille souhaitee du fichier. */
static int increase_file(struct vfat_instance_struct *vfat_instance, unsigned long *first_cluster, unsigned long *file_size, unsigned long size)
{
  int rv;
  unsigned long clus_count;
 
  clus_count = count_clusters(vfat_instance, size);
  if ((rv = increase_clusterchain(vfat_instance, first_cluster, clus_count, &clus_count)) < 0)
    return rv;
  *file_size = min(size, clus_count << vfat_instance->log_clus_size); /* nouvelle taille du fichier */
  return 0;
}
 
/* Agrandit un repertoire.
 * dir_ent_key est la cle a partir de laquelle on souhaite agrandir le
 * repertoire.
 * count est le nombre souhaite de clusters a ajouter. */
static int increase_dir(struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key, unsigned long count)
{
  int rv;
  unsigned long sec_num;
  unsigned long cluster;
  unsigned long tot_count;
 
  sec_num = get_dir_ent_key_sec_num(vfat_instance, dir_ent_key);
  if (sec_num < vfat_instance->fat_info.FirstDataSector) /* repertoire racine */
    return -ENOSPC; /* le repertoire racine ne peut pas etre agrandi */
  /* allocation de nouveaux clusters pour le repertoire */
  cluster = get_cluster_of_sector(vfat_instance, sec_num);
  if ((rv = increase_clusterchain(vfat_instance, &cluster, 1 + count, &tot_count)) < 0)
    return rv;
  /* initialisation des nouveaux clusters */
  if ((rv = vfat_traverse_clear_dir(vfat_instance, dir_ent_key)) < 0)
    return rv;
  if (tot_count < 1 + count) /* pas assez de place sur le disque */
    return -ENOSPC;
  return 0;
}
 
static int prepare_create_name(struct vfat_instance_struct *vfat_instance, unsigned long first_dir_ent_key, const char *fs_basename, unsigned short longname[], unsigned char shortname[], unsigned long *dir_ent_key)
{
  int rv;
  unsigned int dir_ent_cnt;
  unsigned long tot_dir_ent_cnt;
  unsigned long clus_cnt;
 
  /* generation des noms */
  if ((rv = generate_names(vfat_instance, first_dir_ent_key, fs_basename, longname, shortname)) < 0)
    return rv;
  /* calcul du nombre d'entrees de repertoire necessaires */
  dir_ent_cnt = count_dir_entries(vfat_instance, longname, shortname);
  /* verification que le nom n'existe pas et recherche d'une place dans le
     repertoire pour ecrire l'entree du nouveau fichier */
  if ((rv = vfat_traverse_prepare_write_name(vfat_instance, first_dir_ent_key, &tot_dir_ent_cnt, fs_basename, dir_ent_cnt, dir_ent_key)) < 0)
    return rv;
  if (rv > 0) { /* le repertoire doit etre agrandi */
    clus_cnt = count_clusters(vfat_instance, rv << 5); /* nombre de clusters supplementaires a ajouter */
    if (tot_dir_ent_cnt > 65536 - (clus_cnt << (vfat_instance->log_clus_size - 5))) /* la taille d'un repertoire ne doit pas depasser 65536 * 32 */
      return -ENOSPC;
    if ((rv = increase_dir(vfat_instance, *dir_ent_key, clus_cnt)) < 0)
      return rv;
  }
  return 0;
}
 
static int create_name(struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key, const unsigned short longname[], const unsigned char shortname[], const struct vfat_attr_struct *attr)
{
  /* ecriture des entrees de repertoire */
#if VFAT_USE_LONG_NAMES != 0
  return vfat_traverse_write_name(vfat_instance, dir_ent_key, longname, shortname, attr);
#else
  return vfat_traverse_write_name(vfat_instance, dir_ent_key, NULL, shortname, attr);
#endif
}
 
static int read(struct vfat_instance_struct *vfat_instance, unsigned long first_cluster, unsigned long file_size, unsigned long pos, char *fs_buf, unsigned int count)
{
  int rv;
  unsigned long file_blk_key;
 
  if (pos >= file_size)
    return 0; /* rien a ecrire */
  if (count > file_size)
    count = file_size;
  if (pos > file_size - count) /* debordement possible */
    count = file_size - pos; /* on ne doit pas lire plus que la taille du fichier */
  /* count ne peut pas etre devenu nul par ses operations. */
  /* A ce point, on a ramene le probleme a lire count >= 1 octets a la position pos. */
  /* lecture du fichier */
  if (!first_cluster)
    return -EINVAL;
  file_blk_key = get_file_blk_key(vfat_instance, first_cluster, 0);
  if ((rv = skip(vfat_instance, &file_blk_key, pos / vfat_instance->file_blk_size)) < 0)
    return rv;
  pos &= vfat_instance->file_blk_size - 1;
  return vfat_traverse_read(vfat_instance, file_blk_key, pos, fs_buf, count);
}
 
static int write(struct vfat_instance_struct *vfat_instance, unsigned long *first_cluster, unsigned long *file_size, unsigned long pos, const char *fs_buf, unsigned int count)
{
  int rv;
  unsigned long pos_zeros;
  unsigned long file_blk_key;
 
  pos_zeros = min(*file_size, pos); /* s'il y a un trou, il devra etre rempli par des zeros. */
  /* ajustement du nombre d'octets a ecrire */
  if (pos > ULONG_MAX - count) /* pas d'overflow */
    count = ULONG_MAX - pos;
  if (pos + count > *file_size) {
    /* La taille du fichier doit etre augmentee. */
    if ((rv = increase_file(vfat_instance, first_cluster, file_size, pos + count)) < 0)
      return rv;
    /* La nouvelle taille du fichier peut etre inferieure a celle demandee. */
    if (pos > *file_size)
      pos = *file_size; /* il peut y avoir des zeros a ecrire */
    if (count > *file_size)
      count = *file_size;
    if (pos > *file_size - count) /* debordement possible */
      count = *file_size - pos; /* on ne doit pas ecrire plus que la taille du fichier */
  }
  /* count ne peut pas etre devenu nul par ses operations. */
  /* A ce point, on a ramene le probleme a ecrire count >= 1 octets a la position pos. */
  /* ecriture dans le fichier */
  if (!*first_cluster)
    return -EINVAL;
  file_blk_key = get_file_blk_key(vfat_instance, *first_cluster, 0);
  if ((rv = skip(vfat_instance, &file_blk_key, pos_zeros / vfat_instance->file_blk_size)) < 0)
    return rv;
  pos -= pos_zeros & ~(vfat_instance->file_blk_size - 1);
  pos_zeros &= vfat_instance->file_blk_size - 1;
  return vfat_traverse_write(vfat_instance, file_blk_key, pos_zeros, pos, fs_buf, count);
}
 
/* Interface de vfat_instance */
 
int vfat_instance_init(struct vfat_instance_struct *vfat_instance, const struct device_struct *device)
{
  int retval;
  void *buf;
  FSInfo_t *fs_info;
  unsigned long val;
 
  vfat_instance->device = device;
 
  /* lecture du secteur de boot */
  if (!(buf = buffer_get(vfat_instance->device, 0))) {
    retval = -EIO;
    goto error;
  }
  if (!buffer_update(buf)) {
    retval = -EIO;
    goto error_buffer;
  }
  if (!init_fat_info(&vfat_instance->fat_info, buf)) {
    retval = -EINVAL;
    goto error_buffer;
  }
  buffer_release(buf);
 
  /* calcul de valeurs utiles et premieres verifications */
  if (vfat_instance->fat_info.FATType == FAT32 && vfat_instance->fat_info.boot_sect.fat32.BPB_FSVer != ((MAJOR_REVISION_NUMBER << 8) | MINOR_REVISION_NUMBER))
    return -EINVAL;
  vfat_instance->log_sec_size = ulog2(vfat_instance->fat_info.boot_sect.BPB_BytsPerSec);
  vfat_instance->log_blk_size = ulog2(BLOCK_SIZE);
  vfat_instance->clus_size = (unsigned long)vfat_instance->fat_info.boot_sect.BPB_SecPerClus << vfat_instance->log_sec_size;
  vfat_instance->log_clus_size = ulog2(vfat_instance->clus_size);
  if (!check_tot_sec(vfat_instance))
    return -EINVAL;
  if (vfat_instance->fat_info.FATType == FAT32) {
    vfat_instance->mirror = !(vfat_instance->fat_info.boot_sect.fat32.BPB_ExtFlags & 0x80);
    if (!vfat_instance->mirror)
      vfat_instance->active_fat = vfat_instance->fat_info.boot_sect.fat32.BPB_ExtFlags & 0xf;
  }
  else
    vfat_instance->mirror = 1;
  vfat_instance->file_blk_size = min(vfat_instance->fat_info.boot_sect.BPB_BytsPerSec, BLOCK_SIZE);
 
  /* clusters */
  mutex_init(&vfat_instance->alloc_mutex);
  if ((retval = vfat_cluster_set_init(&vfat_instance->free_cluster_set, VFAT_FREE_CLUSTER_TABLE_SIZE)) < 0)
    goto error;
  if (vfat_instance->fat_info.FATType == FAT32) {
    /* lecture du FSInfo */
    if (!(buf = buffer_get(vfat_instance->device, vfat_instance->fat_info.boot_sect.fat32.BPB_FSInfo))) {
      retval = -EIO;
      goto error_destroy_cluster_set;
    }
    if (!buffer_update(buf)) {
      retval = -EIO;
      goto error_destroy_cluster_set_buffer;
    }
    fs_info = (FSInfo_t *)buf;
    if (fs_info->FSI_LeadSig != 0x41615252) {
      retval = -EINVAL;
      goto error_destroy_cluster_set_buffer;
    }
    vfat_instance->next_free = is_valid_data_cluster_number(vfat_instance, fs_info->FSI_Nxt_Free) ? fs_info->FSI_Nxt_Free : 0;
    if (fs_info->FSI_StrucSig != 0x61417272) {
      retval = -EINVAL;
      goto error_destroy_cluster_set_buffer;
    }
    if (fs_info->FSI_TrailSig != 0xaa550000) {
      retval = -EINVAL;
      goto error_destroy_cluster_set_buffer;
    }
    buffer_release(buf);
  }
  else
    vfat_instance->next_free = 2; /* premier numero de cluster de donnees */
  vfat_instance->last_free = vfat_instance->next_free - 1;
 
  /* verrous */
  if ((retval = vfat_lock_pool_init(&vfat_instance->lock_pool, VFAT_LOCK_TABLE_SIZE)) < 0)
    goto error_destroy_cluster_set;
 
  /* indicateurs */
  vfat_instance->read_only = !buffer_is_writable(vfat_instance->device);
  vfat_instance->error = 0;
 
  /* les deux premiers clusters doivent etre non nuls */
  if (!get_fat_entry(vfat_instance, 0, &val)) {
    retval = -EIO;
    goto error_lock_pool_destroy;
  }
  if (!val) {
    retval = -EINVAL;
    goto error_lock_pool_destroy;
  }
  if (!get_fat_entry(vfat_instance, 1, &val)) {
    retval = -EIO;
    goto error_lock_pool_destroy;
  }
  if (!val) {
    retval = -EINVAL;
    goto error_lock_pool_destroy;
  }
 
  if (!vfat_instance->read_only && vfat_instance->fat_info.FATType != FAT12) {
    if (get_fat_entry(vfat_instance, 1, &val))
      switch (vfat_instance->fat_info.FATType) {
      case FAT16:
        val &= ~0x8000; /* dirty */
        val |= 0x4000;  /* no disk read/write error */
        break;
      case FAT32:
        val &= ~0x08000000; /* dirty */
        val |= 0x04000000;  /* no disk read/write error */
        break;
      }
      set_fat_entry(vfat_instance, 1, val);
    }
 
  sync(vfat_instance);
  return 0;
 
 error_destroy_cluster_set_buffer:
  buffer_release(buf);
  goto error_destroy_cluster_set;
 error_buffer:
  buffer_release(buf);
  goto error;
 
 error_lock_pool_destroy:
  vfat_lock_pool_destroy(&vfat_instance->lock_pool);
 error_destroy_cluster_set:
  vfat_cluster_set_destroy(&vfat_instance->free_cluster_set);
 error:
  return retval;
}
 
void vfat_instance_destroy(struct vfat_instance_struct *vfat_instance)
{
  void *buf;
  FSInfo_t *fs_info;
  unsigned long val;
 
  if (!vfat_instance->read_only) {
    /* ecriture du FSInfo */
    if (vfat_instance->fat_info.FATType == FAT32) {
      if (!(buf = buffer_get(vfat_instance->device, vfat_instance->fat_info.boot_sect.fat32.BPB_FSInfo)))
        goto _1;
      if (!buffer_update(buf))
        goto buffer_release;
      fs_info = (FSInfo_t *)buf;
      fs_info->FSI_Free_Count = 0xffffffff; /* unknown */
      fs_info->FSI_Nxt_Free = is_valid_data_cluster_number(vfat_instance, vfat_instance->next_free) ? vfat_instance->next_free : 0xffffffff;
      buffer_dirty(buf);
    buffer_release:
      buffer_release(buf);
    }
  _1:
 
    if (vfat_instance->fat_info.FATType != FAT12) {
      if (get_fat_entry(vfat_instance, 1, &val)) {
        switch (vfat_instance->fat_info.FATType) {
        case FAT16:
          val |= 0x8000; /* clean */
          break;
        case FAT32:
          val |= 0x08000000; /* clean */
          break;
        }
        set_fat_entry(vfat_instance, 1, val);
      }
    }
 
    sync(vfat_instance);
  }
 
  vfat_cluster_set_destroy(&vfat_instance->free_cluster_set);
  vfat_lock_pool_destroy(&vfat_instance->lock_pool);
}
 
int vfat_instance_find(struct vfat_instance_struct *vfat_instance, unsigned long dir_id, const char *fs_basename, unsigned long *id, int *type)
{
  int retval;
  struct vfat_attr_struct attr;
  unsigned long fst_dir_ent_key;
 
  if (!vfat_lock(&vfat_instance->lock_pool, dir_id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du repertoire */
  if (dir_id)
    if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
  /* calcul de la premiere cle du repertoire */
  if (dir_id && !check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock;
  }
  fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, dir_id ? get_first_sec_num(vfat_instance, dir_id, attr.first_cluster) : get_root_first_sec_num(vfat_instance));
  /* recherche du nom */
  retval = vfat_traverse_find(vfat_instance, fst_dir_ent_key, fs_basename, id, type);
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, dir_id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_get_name(struct vfat_instance_struct *vfat_instance, unsigned long id, char name[])
{
  int retval;
 
  if (!vfat_lock(&vfat_instance->lock_pool, id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  retval = vfat_traverse_get_name(vfat_instance, id, name);
  vfat_unlock(&vfat_instance->lock_pool, id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_get_size(struct vfat_instance_struct *vfat_instance, unsigned long reg_id, unsigned long *size)
{
  int retval;
  struct vfat_attr_struct attr;
 
  if (!vfat_lock(&vfat_instance->lock_pool, reg_id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  if (!reg_id)
    *size = 0;
  else {
    if ((retval = vfat_traverse_get_attr(vfat_instance, reg_id, &attr)) < 0)
      goto unlock;
    *size = attr.size;
  }
  retval = 0;
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, reg_id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_stat(struct vfat_instance_struct *vfat_instance, unsigned long id, struct stat_struct *fs_buf)
{
  struct stat_struct statbuf;
  int retval;
  struct vfat_attr_struct attr;
 
  if (!verify_area(fs_buf, sizeof (struct stat_struct), PF_WRITE))
    return -EFAULT;
 
  if (!vfat_lock(&vfat_instance->lock_pool, id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  statbuf.dev = vfat_instance->device->id;
  statbuf.id = id;
  if (!id) {
    statbuf.type = FT_DIR;
    statbuf.size = 0;
    statbuf.mtime = 0;
  }
  else {
    if ((retval = vfat_traverse_get_attr(vfat_instance, id, &attr)) < 0)
      goto unlock;
    statbuf.type = attr.type;
    statbuf.size = attr.size;
    statbuf.mtime = make_time(&attr.mtime);
  }
  statbuf.rdev = DEV_NONE;
  memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
  retval = 0;
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_read_dir(struct vfat_instance_struct *vfat_instance, unsigned long dir_id, unsigned long pos, char *fs_buf, unsigned int count)
{
  int retval;
  struct vfat_attr_struct attr;
  unsigned long fst_dir_ent_key;
  int n;
  struct calendar_time_struct adate;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  if (!vfat_lock(&vfat_instance->lock_pool, dir_id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du repertoire */
  if (dir_id)
    if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
  /* calcul de la premiere cle du repertoire */
  if (dir_id && !check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock;
  }
  fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, dir_id ? get_first_sec_num(vfat_instance, dir_id, attr.first_cluster) : get_root_first_sec_num(vfat_instance));
  /* lecture du repertoire */
  if ((retval = vfat_traverse_read_dir(vfat_instance, fst_dir_ent_key, pos, fs_buf, count)) < 0)
    goto unlock;
  n = retval;
  /* mise a jour de l'entree de repertoire */
  get_calendar_time(&adate);
  if (!vfat_instance->read_only && dir_id && datecmp(&adate, &attr.adate) > 0) {
    vfat_unlock(&vfat_instance->lock_pool, dir_id);
    if (!vfat_lock(&vfat_instance->lock_pool, dir_id, lr_write)) {
      retval = -EIO;
      goto sync;
    }
    /* on relit les attributs car on a relache le verrou depuis la premiere lecture */
    if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
    get_calendar_time(&adate);
    if (datecmp(&adate, &attr.adate) > 0) {
      attr.adate = adate;
      if ((retval = vfat_traverse_set_attr(vfat_instance, dir_id, &attr)) < 0)
        goto unlock;
    }
  }
  retval = n;
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, dir_id);
 sync:
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_read(struct vfat_instance_struct *vfat_instance, unsigned long leaf_id, unsigned long pos, char *fs_buf, unsigned int count)
{
  int retval;
  struct vfat_attr_struct attr;
  int n;
  struct calendar_time_struct adate;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  if (!vfat_lock(&vfat_instance->lock_pool, leaf_id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du fichier */
  if ((retval = vfat_traverse_get_attr(vfat_instance, leaf_id, &attr)) < 0)
    goto unlock;
  if (!check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock;
  }
  if ((retval = read(vfat_instance, attr.first_cluster, attr.size, pos, fs_buf, count)) < 0)
    goto unlock;
  n = retval;
  /* mise a jour de l'entree de repertoire */
  get_calendar_time(&adate);
  if (!vfat_instance->read_only && datecmp(&adate, &attr.adate) > 0) {
    vfat_unlock(&vfat_instance->lock_pool, leaf_id);
    if (!vfat_lock(&vfat_instance->lock_pool, leaf_id, lr_write)) {
      retval = -EIO;
      goto sync;
    }
    /* on relit les attributs car on a relache le verrou depuis la premiere lecture */
    if ((retval = vfat_traverse_get_attr(vfat_instance, leaf_id, &attr)) < 0)
      goto unlock;
    get_calendar_time(&adate);
    if (datecmp(&adate, &attr.adate) > 0) {
      attr.adate = adate;
      if ((retval = vfat_traverse_set_attr(vfat_instance, leaf_id, &attr)) < 0)
        goto unlock;
    }
  }
  retval = n;
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, leaf_id);
 sync:
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_write(struct vfat_instance_struct *vfat_instance, unsigned long leaf_id, unsigned long pos, const char *fs_buf, unsigned int count)
{
  int retval;
  struct vfat_attr_struct attr;
  int n;
 
  if (vfat_instance->read_only)
    return -EROFS;
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_READ))
    return -EFAULT;
 
  if (!vfat_lock(&vfat_instance->lock_pool, leaf_id, lr_write)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du fichier */
  if ((retval = vfat_traverse_get_attr(vfat_instance, leaf_id, &attr)) < 0)
    goto unlock;
  if (attr.flags.read_only) {
    retval = -EACCES;
    goto unlock;
  }
  /* ecriture dans le fichier */
  if (!check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock;
  }
  if ((retval = write(vfat_instance, &attr.first_cluster, &attr.size, pos, fs_buf, count)) < 0)
    goto unlock;
  n = retval;
  /* mise a jour de l'entree de repertoire */
  attr.flags.archive = 1;
  get_calendar_time(&attr.mtime);
  attr.adate = attr.mtime;
  if ((retval = vfat_traverse_set_attr(vfat_instance, leaf_id, &attr)) < 0)
    goto unlock;
  retval = n;
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, leaf_id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_create(struct vfat_instance_struct *vfat_instance, unsigned long dir_id, const char *fs_basename)
{
  int retval;
  struct vfat_attr_struct attr;
  unsigned long fst_dir_ent_key;
  unsigned short longname[256];
  unsigned char shortname[11];
  unsigned long dir_ent_key;
  struct vfat_attr_struct new_attr;
 
  if (vfat_instance->read_only)
    return -EROFS;
 
  if (!vfat_lock(&vfat_instance->lock_pool, dir_id, lr_write)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du repertoire */
  if (dir_id)
    if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
  /* calcul de la premiere cle du repertoire */
  if (dir_id && !check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock;
  }
  fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, dir_id ? get_first_sec_num(vfat_instance, dir_id, attr.first_cluster) : get_root_first_sec_num(vfat_instance));
  /* preparation de la creation du nom */
  if ((retval = prepare_create_name(vfat_instance, fst_dir_ent_key, fs_basename, longname, shortname, &dir_ent_key)) < 0)
    goto unlock;
  /* ecriture des entrees de repertoire du nouveau fichier */
  new_attr.first_cluster = 0;
  new_attr.type = FT_REG;
  new_attr.size = 0;
  new_attr.flags.read_only = 0;
  new_attr.flags.archive = 1;
  get_calendar_time(&new_attr.ctime);
  new_attr.mtime = new_attr.adate = new_attr.ctime;
  if ((retval = create_name(vfat_instance, dir_ent_key, longname, shortname, &new_attr)) < 0)
    goto unlock;
  /* mise a jour de l'entree de repertoire du repertoire parent */
  if (dir_id) {
    get_calendar_time(&attr.mtime);
    attr.adate = attr.mtime;
    if ((retval = vfat_traverse_set_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
  }
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, dir_id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_mkdir(struct vfat_instance_struct *vfat_instance, unsigned long dir_id, const char *fs_basename)
{
  int retval;
  struct vfat_attr_struct attr;
  unsigned long fst_dir_ent_key;
  unsigned short longname[256];
  unsigned char shortname[11];
  unsigned long dir_ent_key;
  unsigned long fst_clus;
  struct vfat_attr_struct new_attr;
 
  if (vfat_instance->read_only)
    return -EROFS;
 
  if (!vfat_lock(&vfat_instance->lock_pool, dir_id, lr_write)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du repertoire */
  if (dir_id)
    if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
  /* calcul de la premiere cle du repertoire */
  if (dir_id && !check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock;
  }
  fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, dir_id ? get_first_sec_num(vfat_instance, dir_id, attr.first_cluster) : get_root_first_sec_num(vfat_instance));
  /* preparation de la creation du nom */
  if ((retval = prepare_create_name(vfat_instance, fst_dir_ent_key, fs_basename, longname, shortname, &dir_ent_key)) < 0)
    goto unlock;
  /* creation du nouveau repertoire */
  if ((retval = create_dir(vfat_instance, &fst_clus)) < 0)
    goto unlock;
  /* ecriture des entrees de repertoire du nouveau repertoire */
  new_attr.first_cluster = fst_clus;
  new_attr.type = FT_DIR;
  new_attr.size = 0;
  new_attr.flags.read_only = 0;
  new_attr.flags.archive = 0;
  get_calendar_time(&new_attr.ctime);
  new_attr.mtime = new_attr.adate = new_attr.ctime;
  if ((retval = create_name(vfat_instance, dir_ent_key, longname, shortname, &new_attr)) < 0)
    goto unlock;
  /* ecriture de . et .. */
  /* Un cluster est toujours suffisant pour creer . et .. . */
  if ((retval = vfat_traverse_write_dot_dotdot(vfat_instance, get_first_dir_ent_key(vfat_instance, FirstSectorofCluster(&vfat_instance->fat_info, fst_clus)), fst_clus, dir_id ? attr.first_cluster : 0, &new_attr.mtime, &new_attr.adate, &new_attr.ctime)) < 0)
    goto unlock;
  /* mise a jour de l'entree de repertoire du repertoire parent */
  if (dir_id) {
    get_calendar_time(&attr.mtime);
    attr.adate = attr.mtime;
    if ((retval = vfat_traverse_set_attr(vfat_instance, dir_id, &attr)) < 0)
      goto unlock;
  }
 unlock:
  vfat_unlock(&vfat_instance->lock_pool, dir_id);
  sync(vfat_instance);
 end:
  return retval;
}
 
int vfat_instance_can_remove(struct vfat_instance_struct *vfat_instance, unsigned long leaf_id)
{
  return vfat_instance->read_only ? -EROFS : 0;
}
 
int vfat_instance_remove(struct vfat_instance_struct *vfat_instance, unsigned long leaf_id, unsigned long parent_dir_id)
{
  int retval;
  struct vfat_attr_struct attr;
 
  if (vfat_instance->read_only)
    return -EROFS;
 
  if (!vfat_lock(&vfat_instance->lock_pool, leaf_id, lr_write)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du fichier */
  if ((retval = vfat_traverse_get_attr(vfat_instance, leaf_id, &attr)) < 0)
    goto unlock_leaf_id;
  /* suppression de l'entree de repertoire */
  if ((retval = vfat_traverse_clear_name(vfat_instance, leaf_id)) < 0)
    goto unlock_leaf_id;
  /* liberation des clusters du fichier */
  if (!check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock_leaf_id;
  }
  if (attr.first_cluster)
    if ((retval = free_clusterchain(vfat_instance, attr.first_cluster)) < 0)
      goto unlock_leaf_id;
  vfat_unlock(&vfat_instance->lock_pool, leaf_id);
 
  if (parent_dir_id) {
    /* mise a jour de l'entree de repertoire du repertoire parent */
    if (!vfat_lock(&vfat_instance->lock_pool, parent_dir_id, lr_write)) {
      retval = -EIO;
      goto sync;
    }
    if ((retval = vfat_traverse_get_attr(vfat_instance, parent_dir_id, &attr)) < 0)
      goto unlock_parent_dir_id;
    get_calendar_time(&attr.mtime);
    attr.adate = attr.mtime;
    if ((retval = vfat_traverse_set_attr(vfat_instance, parent_dir_id, &attr)) < 0)
      goto unlock_parent_dir_id;
    vfat_unlock(&vfat_instance->lock_pool, parent_dir_id);
  }
 
  retval = 0;
 sync:
  sync(vfat_instance);
 end:
  return retval;
 unlock_parent_dir_id:
  vfat_unlock(&vfat_instance->lock_pool, parent_dir_id);
  goto sync;
 unlock_leaf_id:
  vfat_unlock(&vfat_instance->lock_pool, leaf_id);
  goto sync;
}
 
int vfat_instance_rmdir(struct vfat_instance_struct *vfat_instance, unsigned long dir_id, unsigned long parent_dir_id)
{
  int retval;
  struct vfat_attr_struct attr;
  unsigned long fst_dir_ent_key;
 
  if (vfat_instance->read_only)
    return -EROFS;
 
  if (!vfat_lock(&vfat_instance->lock_pool, dir_id, lr_write)) {
    retval = -EIO;
    goto end;
  }
  /* lecture des attributs du repertoire */
  /* On sait que dir_id n'est pas nul. */
  if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
    goto unlock_dir_id;
  /* calcul de la premiere cle du repertoire */
  if (!check_first_cluster(vfat_instance, attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock_dir_id;
  }
  fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, get_first_sec_num(vfat_instance, dir_id, attr.first_cluster));
  /* verification que le repertoire est vide */
  if ((retval = vfat_traverse_is_empty(vfat_instance, fst_dir_ent_key)) < 0)
    goto unlock_dir_id;
  if (!retval) {
    retval = -ENOTEMPTY;
    goto unlock_dir_id;
  }
  /* suppression de l'entree de repertoire */
  if ((retval = vfat_traverse_clear_name(vfat_instance, dir_id)) < 0)
    goto unlock_dir_id;
  /* liberation des clusters du fichier */
  if (attr.first_cluster)
    if ((retval = free_clusterchain(vfat_instance, attr.first_cluster)) < 0)
      goto unlock_dir_id;
  vfat_unlock(&vfat_instance->lock_pool, dir_id);
 
  if (parent_dir_id) {
    /* mise a jour de l'entree de repertoire du repertoire parent */
    if (!vfat_lock(&vfat_instance->lock_pool, parent_dir_id, lr_write)) {
      retval = -EIO;
      goto sync;
    }
    if ((retval = vfat_traverse_get_attr(vfat_instance, parent_dir_id, &attr)) < 0)
      goto unlock_parent_dir_id;
    get_calendar_time(&attr.mtime);
    attr.adate = attr.mtime;
    if ((retval = vfat_traverse_set_attr(vfat_instance, parent_dir_id, &attr)) < 0)
      goto unlock_parent_dir_id;
    vfat_unlock(&vfat_instance->lock_pool, parent_dir_id);
  }
 
  retval = 0;
 sync:
  sync(vfat_instance);
 end:
  return retval;
 unlock_parent_dir_id:
  vfat_unlock(&vfat_instance->lock_pool, parent_dir_id);
  goto sync;
 unlock_dir_id:
  vfat_unlock(&vfat_instance->lock_pool, dir_id);
  goto sync;
}
 
/* Afin d'exclure tout risque d'interblocage, on ne verrouille qu'un seul
 * fichier a la fois. Mais du coup on n'est pas a l'abri de modifications
 * concurrentes sur les fichiers et repertoires mis en jeu. On a donc un
 * risque que le systeme de fichiers devienne incoherent. */
int vfat_instance_rename(struct vfat_instance_struct *vfat_instance, unsigned long old_id, unsigned long old_parent_dir_id, unsigned long new_dir_id, const char *fs_basename)
{
  int retval;
  struct vfat_attr_struct attr;
  struct vfat_attr_struct new_dir_attr;
  unsigned long fst_dir_ent_key;
  unsigned short longname[256];
  unsigned char shortname[11];
  unsigned long dir_ent_key;
  struct vfat_attr_struct old_parent_dir_attr;
 
  if (vfat_instance->read_only)
    return -EROFS;
 
  /* On sait que old_id n'est pas nul. */
 
  /* preliminaires */
  if (!vfat_lock(&vfat_instance->lock_pool, old_id, lr_read)) {
    retval = -EIO;
    goto end;
  }
  /* cas ou le nom identifie deja le fichier a renommer */
  if (new_dir_id == old_parent_dir_id) { /* meme repertoire */
    if ((retval = vfat_traverse_match(vfat_instance, old_id, fs_basename)) < 0) /* noms concordants */
      goto unlock_old_id;
    if (retval) {
      retval = 0;
      goto unlock_old_id;
    }
  }
  /* lecture des attributs du fichier */
  if ((retval = vfat_traverse_get_attr(vfat_instance, old_id, &attr)) < 0)
    goto unlock_old_id;
  vfat_unlock(&vfat_instance->lock_pool, old_id);
 
  if (!vfat_lock(&vfat_instance->lock_pool, new_dir_id, lr_write)) {
    retval = -EIO;
    goto sync;
  }
  /* lecture des attributs du repertoire */
  if (new_dir_id)
    if ((retval = vfat_traverse_get_attr(vfat_instance, new_dir_id, &new_dir_attr)) < 0)
      goto unlock_new_dir_id;
  /* calcul de la premiere cle du repertoire */
  if (new_dir_id && !check_first_cluster(vfat_instance, new_dir_attr.first_cluster)) {
    retval = -EINVAL;
    goto unlock_new_dir_id;
  }
  fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, new_dir_id ? get_first_sec_num(vfat_instance, new_dir_id, new_dir_attr.first_cluster) : get_root_first_sec_num(vfat_instance));
  /* preparation de la creation du nouveau nom */
  if ((retval = prepare_create_name(vfat_instance, fst_dir_ent_key, fs_basename, longname, shortname, &dir_ent_key)) < 0)
    goto unlock_new_dir_id;
  /* creation du nouveau nom */
  attr.flags.archive = attr.type == FT_DIR ? 1 : 0;
  if ((retval = create_name(vfat_instance, dir_ent_key, longname, shortname, &attr)) < 0)
    goto unlock_new_dir_id;
  /* mise a jour de l'entree de repertoire du repertoire parent */
  if (new_dir_id) {
    get_calendar_time(&attr.mtime);
    attr.adate = attr.mtime;
    if ((retval = vfat_traverse_set_attr(vfat_instance, new_dir_id, &new_dir_attr)) < 0)
      goto unlock_new_dir_id;
  }
  vfat_unlock(&vfat_instance->lock_pool, new_dir_id);
 
  if (!vfat_lock(&vfat_instance->lock_pool, old_id, lr_write)) {
    retval = -EIO;
    goto sync;
  }
  /* si le fichier est un repertoire, mise a jour de son entree dotdot */
  if (attr.type == FT_DIR) {
    /* calcul de la premiere cle du repertoire */
    if (!check_first_cluster(vfat_instance, attr.first_cluster)) {
      retval = -EINVAL;
      goto unlock_old_id;
    }
    fst_dir_ent_key = get_first_dir_ent_key(vfat_instance, get_first_sec_num(vfat_instance, old_id, attr.first_cluster));
    /* mise a jour de l'entree dotdot */
    if ((retval = vfat_traverse_update_dotdot(vfat_instance, fst_dir_ent_key, new_dir_id ? new_dir_attr.first_cluster : 0)) < 0)
      goto unlock_old_id;
  }
  /* suppression de l'ancien nom */
  if ((retval = vfat_traverse_clear_name(vfat_instance, old_id)) < 0)
    goto unlock_old_id;
  vfat_unlock(&vfat_instance->lock_pool, old_id);
 
  if (old_parent_dir_id) {
    /* mise a jour de l'entree de repertoire du repertoire parent */
    if (!vfat_lock(&vfat_instance->lock_pool, old_parent_dir_id, lr_write)) {
      retval = -EIO;
      goto sync;
    }
    if ((retval = vfat_traverse_get_attr(vfat_instance, old_parent_dir_id, &old_parent_dir_attr)) < 0)
      goto unlock_old_parent_dir_id;
    get_calendar_time(&old_parent_dir_attr.mtime);
    old_parent_dir_attr.adate = old_parent_dir_attr.mtime;
    if ((retval = vfat_traverse_set_attr(vfat_instance, old_parent_dir_id, &old_parent_dir_attr)) < 0)
      goto unlock_old_parent_dir_id;
    vfat_unlock(&vfat_instance->lock_pool, old_parent_dir_id);
  }
 
  retval = 0;
 sync:
  sync(vfat_instance);
 end:
  return retval;
 unlock_new_dir_id:
  vfat_unlock(&vfat_instance->lock_pool, new_dir_id);
  goto sync;
 unlock_old_id:
  vfat_unlock(&vfat_instance->lock_pool, old_id);
  goto sync;
 unlock_old_parent_dir_id:
  vfat_unlock(&vfat_instance->lock_pool, old_parent_dir_id);
  goto sync;
}
 
/* vfat_instance_collide */
 
static int vfat_instance_collide_collide(struct vfat_collide_fo_struct *collide_fo, const unsigned char shortname[])
{
  struct vfat_instance_collide_fo_struct *vfat_instance_collide_fo;
 
  vfat_instance_collide_fo = downcast(struct vfat_instance_collide_fo_struct, collide_fo, collide_fo);
  return vfat_traverse_collide(vfat_instance_collide_fo->vfat_instance, vfat_instance_collide_fo->dir_ent_key, shortname);
}
 
static const struct vfat_collide_fo_vtbl_struct vfat_instance_collide_collide_fo_vtbl = {
  .collide = vfat_instance_collide_collide
};
 
static void init_vfat_instance_collide_fo(struct vfat_instance_collide_fo_struct *vfat_instance_collide_fo, struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key)
{
  vfat_instance_collide_fo->collide_fo.vptr = &vfat_instance_collide_collide_fo_vtbl;
  vfat_instance_collide_fo->vfat_instance = vfat_instance;
  vfat_instance_collide_fo->dir_ent_key = dir_ent_key;
}
 
/* dir_iter */
 
int vfat_init_dir_iter(struct vfat_dir_iter_struct *dir_iter, struct vfat_instance_struct *vfat_instance, unsigned long dir_ent_key)
{
  int retval;
 
  dir_iter->vfat_instance = vfat_instance;
  dir_iter->dir_ent_key = dir_ent_key;
  if (!(dir_iter->buffer = buffer_get(vfat_instance->device, get_dir_ent_key_blk_num(vfat_instance, dir_iter->dir_ent_key)))) {
    retval = -EIO;
    goto error;
  }
  if (!buffer_update(dir_iter->buffer)) {
    retval = -EIO;
    goto error_release;
  }
  return 0;
 
 error_release:
  buffer_release(dir_iter->buffer);
 error:
  return retval;
}
 
void vfat_destroy_dir_iter(struct vfat_dir_iter_struct *dir_iter)
{
  buffer_release(dir_iter->buffer);
}
 
unsigned long vfat_get_dir_iter_dir_ent_key(const struct vfat_dir_iter_struct *dir_iter)
{
  return dir_iter->dir_ent_key;
}
 
union dir_entry_union *vfat_get_dir_iter_dir_entry(const struct vfat_dir_iter_struct *dir_iter)
{
  return (union dir_entry_union *)(dir_iter->buffer + get_dir_ent_key_blk_offset(dir_iter->vfat_instance, dir_iter->dir_ent_key));
}
 
void vfat_dirty_dir_iter_buffer(const struct vfat_dir_iter_struct *dir_iter)
{
  buffer_dirty(dir_iter->buffer);
}
 
/* Retourne :
 * - un nombre strictement positif en cas de succes ;
 * - 0 si la fin du repertoire est atteinte ;
 * - un nombre strictement negatif en cas d'erreur. */
int vfat_next_dir_iter(struct vfat_dir_iter_struct *dir_iter)
{
  const unsigned long ents_per_sec = dir_iter->vfat_instance->fat_info.boot_sect.BPB_BytsPerSec >> 5;
  const unsigned long sec_per_clus = dir_iter->vfat_instance->fat_info.boot_sect.BPB_SecPerClus;
  const unsigned long first_data_sector = dir_iter->vfat_instance->fat_info.FirstDataSector;
 
  unsigned long blk_num, new_blk_num;
  unsigned long sec_num;
  unsigned long data_sec_num;
  unsigned long clus_idx;
  unsigned long cluster;
  unsigned long new_cluster;
  void *new_buffer;
 
  blk_num = get_dir_ent_key_blk_num(dir_iter->vfat_instance, dir_iter->dir_ent_key);
 
  /* calcul du nouvel dir_ent_key */
  sec_num = get_dir_ent_key_sec_num(dir_iter->vfat_instance, dir_iter->dir_ent_key);
  if (sec_num < first_data_sector) { /* root directory */
    if (get_dir_ent_key_sec_num(dir_iter->vfat_instance, dir_iter->dir_ent_key + 1) == first_data_sector)
      return 0;
    dir_iter->dir_ent_key++;
  }
  else { /* clusterchain */
    data_sec_num = sec_num - first_data_sector;
    clus_idx = (data_sec_num & (sec_per_clus - 1)) * ents_per_sec + get_dir_ent_key_sec_idx(dir_iter->vfat_instance, dir_iter->dir_ent_key); /* indice de dir_ent_key dans le cluster */
    if (clus_idx + 1 == sec_per_clus * ents_per_sec) {
      cluster = data_sec_num / sec_per_clus + 2;
      if (!get_fat_entry(dir_iter->vfat_instance, cluster, &new_cluster))
        return -EIO;
      if (IsEOF(&dir_iter->vfat_instance->fat_info, new_cluster)) /* end of clusterchain */
        return 0;
      if (!is_valid_data_cluster_number(dir_iter->vfat_instance, new_cluster))
        return -EINVAL;
      dir_iter->dir_ent_key = get_first_dir_ent_key(dir_iter->vfat_instance, FirstSectorofCluster(&dir_iter->vfat_instance->fat_info, new_cluster));
    }
    else
      dir_iter->dir_ent_key++;
  }
 
  /* chargement d'un nouveau bloc si necessaire */
  new_blk_num = get_dir_ent_key_blk_num(dir_iter->vfat_instance, dir_iter->dir_ent_key);
  if (new_blk_num != blk_num) {
    if (!(new_buffer = buffer_get(dir_iter->vfat_instance->device, new_blk_num)))
      return -EIO;
    buffer_release(dir_iter->buffer);
    dir_iter->buffer = new_buffer;
    if (!buffer_update(dir_iter->buffer))
      return -EIO;
  }
  return 1;
}
 
/* file_iter */
 
int vfat_init_file_iter(struct vfat_file_iter_struct *file_iter, struct vfat_instance_struct *vfat_instance, unsigned long file_blk_key)
{
  int retval;
 
  file_iter->vfat_instance = vfat_instance;
  file_iter->file_blk_key = file_blk_key;
  if (!(file_iter->buffer = buffer_get(vfat_instance->device, get_file_blk_key_blk_num(vfat_instance, file_blk_key)))) {
    retval = -EIO;
    goto error;
  }
  if (!buffer_update(file_iter->buffer)) {
    retval = -EIO;
    goto error_release;
  }
  return 0;
 
 error_release:
  buffer_release(file_iter->buffer);
 error:
  return retval;
}
 
void vfat_destroy_file_iter(struct vfat_file_iter_struct *file_iter)
{
  buffer_release(file_iter->buffer);
}
 
unsigned long vfat_get_file_iter_file_blk_size(const struct vfat_file_iter_struct *file_iter)
{
  return file_iter->vfat_instance->file_blk_size;
}
 
void *vfat_get_file_iter_file_blk_buffer(const struct vfat_file_iter_struct *file_iter)
{
  return file_iter->buffer + get_file_blk_key_blk_offset(file_iter->vfat_instance, file_iter->file_blk_key);
}
 
void vfat_dirty_file_iter_buffer(const struct vfat_file_iter_struct *file_iter)
{
  buffer_dirty(file_iter->buffer);
}
 
/* Retourne :
 * - un nombre strictement positif en cas de succes ;
 * - 0 si la fin de la chaine de clusters est atteinte ;
 * - un nombre strictement negatif en cas d'erreur. */
int vfat_next_file_iter(struct vfat_file_iter_struct *file_iter)
{
  unsigned long blk_num, new_blk_num;
  unsigned long clus_offset;
  unsigned long cluster, new_cluster;
  void *new_buffer;
 
  blk_num = get_file_blk_key_blk_num(file_iter->vfat_instance, file_iter->file_blk_key);
 
  clus_offset = get_file_blk_key_clus_offset(file_iter->vfat_instance, file_iter->file_blk_key);
  if (clus_offset + file_iter->vfat_instance->file_blk_size == file_iter->vfat_instance->clus_size) {
    cluster = get_file_blk_key_cluster(file_iter->vfat_instance, file_iter->file_blk_key);
    if (!get_fat_entry(file_iter->vfat_instance, cluster, &new_cluster))
      return -EIO;
    if (IsEOF(&file_iter->vfat_instance->fat_info, new_cluster)) /* end of clusterchain */
      return 0;
    if (!is_valid_data_cluster_number(file_iter->vfat_instance, new_cluster))
      return -EINVAL;
    file_iter->file_blk_key = get_file_blk_key(file_iter->vfat_instance, new_cluster, 0);
  }
  else
    file_iter->file_blk_key++;
 
  /* chargement d'un nouveau bloc si necessaire */
  new_blk_num = get_file_blk_key_blk_num(file_iter->vfat_instance, file_iter->file_blk_key);
  if (new_blk_num != blk_num) {
    if (!(new_buffer = buffer_get(file_iter->vfat_instance->device, new_blk_num)))
      return -EIO;
    buffer_release(file_iter->buffer);
    file_iter->buffer = new_buffer;
    if (!buffer_update(file_iter->buffer))
      return -EIO;
  }
  return 1;
}