#include "vfat_instance_struct.h"
#include "vfat_traverse.h"
#include "vfat_iter_struct.h"
#include "vfat_attr_struct.h"
#include "vfat_names.h"
#include "vfat_collide.h"
#include "vfat_cluster_set.h"
#include "vfat_lock.h"
#include "vfat_fat_info.h"
#include "device_struct.h"
#include "buffer.h"
#include <stat_struct.h>
#include <verify_area.h>
#include <segment.h>
#include <time.h>
#include <mutex.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <config.h>
#include <misc.h>
#include <util.h>
#include <string.h>
#include <limits.h>
#define MAJOR_REVISION_NUMBER 0
#define MINOR_REVISION_NUMBER 0
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:
case FAT16:
return 2;
case FAT32:
default:
return 4;
}
}
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;
}
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);
static inline int check_tot_sec(const struct vfat_instance_struct *vfat_instance)
{
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;
}
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);
}
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;
}
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;
}
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;
}
static void sync(struct vfat_instance_struct *vfat_instance)
{
if (!buffer_sync(vfat_instance->device))
set_io_error(vfat_instance);
}
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;
}
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;
}
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;
}
static unsigned long get_free_cluster(struct vfat_instance_struct *vfat_instance)
{
unsigned long n;
unsigned int i;
unsigned long val;
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))
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;
}
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;
}
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;
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);
while (count-- && (next_cluster = alloc_cluster(vfat_instance, cluster ? &cluster : first_cluster))) {
cluster = next_cluster;
(*tot_count)++;
}
return 0;
}
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;
}
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;
break;
case FAT32:
val &= ~0x04000000;
break;
}
set_fat_entry(vfat_instance, 1, val);
}
}
vfat_instance->error = 0;
}
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);
}
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;
}
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;
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))
return -EINVAL;
if (!is_valid_data_cluster_number(vfat_instance, cluster))
return -EINVAL;
}
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))
return -EINVAL;
if (!is_valid_data_cluster_number(vfat_instance, cluster))
return -EINVAL;
}
clus_idx = count;
end:
*file_blk_key = get_file_blk_key(vfat_instance, cluster, clus_idx * vfat_instance->file_blk_size);
return 0;
}
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
}
static int create_dir(struct vfat_instance_struct *vfat_instance, unsigned long *first_cluster)
{
int rv;
unsigned long tot_count;
*first_cluster = 0;
if ((rv = increase_clusterchain(vfat_instance, first_cluster, 1, &tot_count)) < 0)
return rv;
if (tot_count == 0)
return -ENOSPC;
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;
}
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);
return 0;
}
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)
return -ENOSPC;
cluster = get_cluster_of_sector(vfat_instance, sec_num);
if ((rv = increase_clusterchain(vfat_instance, &cluster, 1 + count, &tot_count)) < 0)
return rv;
if ((rv = vfat_traverse_clear_dir(vfat_instance, dir_ent_key)) < 0)
return rv;
if (tot_count < 1 + count)
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;
if ((rv = generate_names(vfat_instance, first_dir_ent_key, fs_basename, longname, shortname)) < 0)
return rv;
dir_ent_cnt = count_dir_entries(vfat_instance, longname, shortname);
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) {
clus_cnt = count_clusters(vfat_instance, rv << 5);
if (tot_dir_ent_cnt > 65536 - (clus_cnt << (vfat_instance->log_clus_size - 5)))
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)
{
#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;
if (count > file_size)
count = file_size;
if (pos > file_size - count)
count = file_size - pos;
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);
if (pos > ULONG_MAX - count)
count = ULONG_MAX - pos;
if (pos + count > *file_size) {
if ((rv = increase_file(vfat_instance, first_cluster, file_size, pos + count)) < 0)
return rv;
if (pos > *file_size)
pos = *file_size;
if (count > *file_size)
count = *file_size;
if (pos > *file_size - count)
count = *file_size - pos;
}
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);
}
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;
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);
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);
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) {
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;
vfat_instance->last_free = vfat_instance->next_free - 1;
if ((retval = vfat_lock_pool_init(&vfat_instance->lock_pool, VFAT_LOCK_TABLE_SIZE)) < 0)
goto error_destroy_cluster_set;
vfat_instance->read_only = !buffer_is_writable(vfat_instance->device);
vfat_instance->error = 0;
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;
val |= 0x4000;
break;
case FAT32:
val &= ~0x08000000;
val |= 0x04000000;
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) {
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;
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;
break;
case FAT32:
val |= 0x08000000;
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;
}
if (dir_id)
if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
goto unlock;
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));
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;
}
if (dir_id)
if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
goto unlock;
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));
if ((retval = vfat_traverse_read_dir(vfat_instance, fst_dir_ent_key, pos, fs_buf, count)) < 0)
goto unlock;
n = retval;
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;
}
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;
}
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;
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;
}
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;
}
if ((retval = vfat_traverse_get_attr(vfat_instance, leaf_id, &attr)) < 0)
goto unlock;
if (attr.flags.read_only) {
retval = -EACCES;
goto unlock;
}
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;
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;
}
if (dir_id)
if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
goto unlock;
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));
if ((retval = prepare_create_name(vfat_instance, fst_dir_ent_key, fs_basename, longname, shortname, &dir_ent_key)) < 0)
goto unlock;
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;
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;
}
if (dir_id)
if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
goto unlock;
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));
if ((retval = prepare_create_name(vfat_instance, fst_dir_ent_key, fs_basename, longname, shortname, &dir_ent_key)) < 0)
goto unlock;
if ((retval = create_dir(vfat_instance, &fst_clus)) < 0)
goto unlock;
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;
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;
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;
}
if ((retval = vfat_traverse_get_attr(vfat_instance, leaf_id, &attr)) < 0)
goto unlock_leaf_id;
if ((retval = vfat_traverse_clear_name(vfat_instance, leaf_id)) < 0)
goto unlock_leaf_id;
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) {
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;
}
if ((retval = vfat_traverse_get_attr(vfat_instance, dir_id, &attr)) < 0)
goto unlock_dir_id;
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));
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;
}
if ((retval = vfat_traverse_clear_name(vfat_instance, dir_id)) < 0)
goto unlock_dir_id;
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) {
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;
}
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;
if (!vfat_lock(&vfat_instance->lock_pool, old_id, lr_read)) {
retval = -EIO;
goto end;
}
if (new_dir_id == old_parent_dir_id) {
if ((retval = vfat_traverse_match(vfat_instance, old_id, fs_basename)) < 0)
goto unlock_old_id;
if (retval) {
retval = 0;
goto unlock_old_id;
}
}
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;
}
if (new_dir_id)
if ((retval = vfat_traverse_get_attr(vfat_instance, new_dir_id, &new_dir_attr)) < 0)
goto unlock_new_dir_id;
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));
if ((retval = prepare_create_name(vfat_instance, fst_dir_ent_key, fs_basename, longname, shortname, &dir_ent_key)) < 0)
goto unlock_new_dir_id;
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;
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;
}
if (attr.type == FT_DIR) {
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));
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;
}
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) {
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;
}
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;
}
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);
}
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);
sec_num = get_dir_ent_key_sec_num(dir_iter->vfat_instance, dir_iter->dir_ent_key);
if (sec_num < first_data_sector) {
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 {
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);
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))
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++;
}
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;
}
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);
}
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))
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++;
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;
}