/* 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 . */
#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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}