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