GEX core repository.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
gex-core/vfs/virtual_fs.c

804 lines
29 KiB

/**
* @file virtual_fs.c
* @brief Implementation of virtual_fs.h
*
* DAPLink Interface Firmware
* Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "platform.h"
#include "virtual_fs.h"
// Virtual file system driver
// Limitations:
// - files must be contiguous
// - data written cannot be read back
// - data should only be read once
// FAT16 limitations +- safety margin
#define FAT_CLUSTERS_MAX (65525 - 100)
#define FAT_CLUSTERS_MIN (4086 + 100)
#define DIRTY_MBR_BOOTCODE 1
typedef struct
{
uint8_t boot_sector[11];
/* DOS 2.0 BPB - Bios Parameter Block, 11 bytes */
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_logical_sectors;
uint8_t num_fats;
uint16_t max_root_dir_entries;
uint16_t total_logical_sectors;
uint8_t media_descriptor;
uint16_t logical_sectors_per_fat;
/* DOS 3.31 BPB - Bios Parameter Block, 12 bytes */
uint16_t physical_sectors_per_track;
uint16_t heads;
uint32_t hidden_sectors;
uint32_t big_sectors_on_drive;
/* Extended BIOS Parameter Block, 26 bytes */
uint8_t physical_drive_number;
uint8_t not_used;
uint8_t boot_record_signature;
uint32_t volume_id;
char volume_label[11];
char file_system_type[8];
#if !DIRTY_MBR_BOOTCODE
/* bootstrap data in bytes 62-509 */
uint8_t bootstrap[448];
/* These entries in place of bootstrap code are the *nix partitions */
//uint8_t partition_one[16];
//uint8_t partition_two[16];
//uint8_t partition_three[16];
//uint8_t partition_four[16];
/* Mandatory value at bytes 510-511, must be 0xaa55 */
uint16_t signature; // but only if bootable...
#endif
} __attribute__((packed)) mbr_t;
typedef struct file_allocation_table
{
uint8_t f[512];
} file_allocation_table_t;
typedef struct FatDirectoryEntry
{
vfs_filename_t filename;
uint8_t attributes;
uint8_t reserved;
uint8_t creation_time_ms;
uint16_t creation_time;
uint16_t creation_date;
uint16_t accessed_date;
uint16_t first_cluster_high_16;
uint16_t modification_time;
uint16_t modification_date;
uint16_t first_cluster_low_16;
uint32_t filesize;
} __attribute__((packed)) FatDirectoryEntry_t;
COMPILER_ASSERT(sizeof(FatDirectoryEntry_t) == 32);
// to save RAM all files must be in the first root dir entry (512 bytes)
// but 2 actually exist on disc (32 entries) to accomodate hidden OS files,
// folders and metadata
typedef struct root_dir
{
FatDirectoryEntry_t f[VFS_MAX_FILES * 2];
} root_dir_t;
typedef struct virtual_media
{
vfs_read_cb_t read_cb;
vfs_write_cb_t write_cb;
uint32_t length;
} virtual_media_t;
static uint32_t read_zero(uint32_t offset, uint8_t *data, uint32_t size);
static void write_none(uint32_t offset, const uint8_t *data, uint32_t size);
static uint32_t read_mbr(uint32_t offset, uint8_t *data, uint32_t size);
static uint32_t read_fat(uint32_t offset, uint8_t *data, uint32_t size);
static uint32_t read_dir(uint32_t offset, uint8_t *data, uint32_t size);
static void write_dir(uint32_t offset, const uint8_t *data, uint32_t size);
static void file_change_cb_stub(const vfs_filename_t filename, vfs_file_change_t change,
vfs_file_t file, vfs_file_t new_file_data);
static uint32_t cluster_to_sector(uint32_t cluster_idx);
static bool filename_valid(const vfs_filename_t filename);
static bool filename_character_valid(char character);
static void set_init_done(void);
#if 0
void unused() {
// Initialize MBR
// memcpy(&mbr, &mbr_tmpl, sizeof(mbr_t));
total_sectors = ((VFS_DISK_SIZE + KB(64)) / VFS_SECTOR_SIZE);
// Make sure this is the right size for a FAT16 volume
if (total_sectors < FAT_CLUSTERS_MIN * VFS_CLUSTER_SIZE) {
assert_param(0);
total_sectors = FAT_CLUSTERS_MIN * VFS_CLUSTER_SIZE;
} else if (total_sectors > FAT_CLUSTERS_MAX * VFS_CLUSTER_SIZE) {
assert_param(0);
total_sectors = FAT_CLUSTERS_MAX * VFS_CLUSTER_SIZE;
}
if (total_sectors >= 0x10000) {
mbr.total_logical_sectors = 0;
mbr.big_sectors_on_drive = total_sectors;
} else {
mbr.total_logical_sectors = total_sectors;
mbr.big_sectors_on_drive = 0;
}
// FAT table will likely be larger than needed, but this is allowed by the
// fat specification
num_clusters = ;
mbr.logical_sectors_per_fat = ;
}
#endif
// If sector size changes update comment below
COMPILER_ASSERT(0x0200 == VFS_SECTOR_SIZE);
// If root directory size changes update max_root_dir_entries
COMPILER_ASSERT(0x0020 == sizeof(root_dir_t) / sizeof(FatDirectoryEntry_t));
#define TOTAL_SECTORS_0 ((VFS_DISK_SIZE + KB(64)) / VFS_SECTOR_SIZE)
#define TOTAL_SECTORS MIN(MAX(TOTAL_SECTORS_0, FAT_CLUSTERS_MIN * VFS_SECTORS_PER_CLUSTER), FAT_CLUSTERS_MAX * VFS_SECTORS_PER_CLUSTER)
#define TOTAL_CLUSTERS (TOTAL_SECTORS / VFS_SECTORS_PER_CLUSTER)
static const mbr_t mbr = {
/*uint8_t[11]*/.boot_sector = {
0xEB, 0x3C, 0x90,
'H', 'A', 'L', '-', '9', '0', '0', '0' // OEM Name in text (8 chars max)
},
/*uint16_t*/.bytes_per_sector = VFS_SECTOR_SIZE, // 512 bytes per sector
/*uint8_t */.sectors_per_cluster = VFS_SECTORS_PER_CLUSTER, // 4k cluster
/*uint16_t*/.reserved_logical_sectors = 0x0001, // mbr is 1 sector
/*uint8_t */.num_fats = 0x02, // 2 FATs
/*uint16_t*/.max_root_dir_entries = 0x0020, // 32 dir entries (max)
/*uint16_t*/.total_logical_sectors = (TOTAL_SECTORS < 0x10000) ? TOTAL_SECTORS
: 0, //0x1f50, // sector size * # of sectors = drive size
/*uint8_t */.media_descriptor = 0xf8, // fixed disc = F8, removable = F0
/*uint16_t*/.logical_sectors_per_fat = (TOTAL_CLUSTERS * 2 + VFS_SECTOR_SIZE - 1) /
VFS_SECTOR_SIZE, //0x0001, // FAT is 1k - ToDO:need to edit this (??)<- comment from DAPLINK
/*uint16_t*/.physical_sectors_per_track = 0x0001, // flat
/*uint16_t*/.heads = 0x0001, // flat
/*uint32_t*/.hidden_sectors = 0x00000000, // before mbt, 0
/*uint32_t*/.big_sectors_on_drive = (TOTAL_SECTORS < 0x10000) ? 0 : TOTAL_SECTORS, // 4k sector. not using large clusters
/*uint8_t */.physical_drive_number = 0x00,
/*uint8_t */.not_used = 0x00, // Current head. Linux tries to set this to 0x1
/*uint8_t */.boot_record_signature = 0x29, // signature is present
/*uint32_t*/.volume_id = 0x27021974, // serial number
// needs to match the root dir label - (update: looks like it does not)
/*char[11]*/.volume_label = {'G', 'E', 'X', '-', 'V', 'F', 'S', '-', 'C', 'F', 'G'},
// unused by msft - just a label (FAT, FAT12, FAT16)
/*char[8] */.file_system_type = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '},
#if !DIRTY_MBR_BOOTCODE
/* Executable boot code that starts the operating system */
/*uint8_t[448]*/.bootstrap = { // TODO get rid of this and read junk instead? Saves 0.5 kB
0x52, 0x6F, 0x6D, 0x2E, 0x20, 0x4F, 0x2C, 0x20, 0x73, 0x70, 0x65, 0x61, 0x6B, 0x20, 0x61, 0x67,
0x61, 0x69, 0x6E, 0x2C, 0x20, 0x62, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x61, 0x6E, 0x67, 0x65,
0x6C, 0x21, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x74, 0x68, 0x6F, 0x75, 0x20, 0x61, 0x72, 0x74, 0x0A,
0x41, 0x73, 0x20, 0x67, 0x6C, 0x6F, 0x72, 0x69, 0x6F, 0x75, 0x73, 0x20, 0x74, 0x6F, 0x20, 0x74,
0x68, 0x69, 0x73, 0x20, 0x6E, 0x69, 0x67, 0x68, 0x74, 0x2C, 0x20, 0x62, 0x65, 0x69, 0x6E, 0x67,
0x20, 0x6F, 0x27, 0x65, 0x72, 0x20, 0x6D, 0x79, 0x20, 0x68, 0x65, 0x61, 0x64, 0x2C, 0x0A, 0x41,
0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x77, 0x69, 0x6E, 0x67, 0x65, 0x64, 0x20, 0x6D, 0x65,
0x73, 0x73, 0x65, 0x6E, 0x67, 0x65, 0x72, 0x20, 0x6F, 0x66, 0x20, 0x68, 0x65, 0x61, 0x76, 0x65,
0x6E, 0x0A, 0x55, 0x6E, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x68, 0x69, 0x74, 0x65,
0x2D, 0x75, 0x70, 0x74, 0x75, 0x72, 0x6E, 0x65, 0x64, 0x20, 0x77, 0x6F, 0x6E, 0x64, 0x27, 0x72,
0x69, 0x6E, 0x67, 0x20, 0x65, 0x79, 0x65, 0x73, 0x0A, 0x4F, 0x66, 0x20, 0x6D, 0x6F, 0x72, 0x74,
0x61, 0x6C, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x66, 0x61, 0x6C, 0x6C, 0x20, 0x62, 0x61,
0x63, 0x6B, 0x20, 0x74, 0x6F, 0x20, 0x67, 0x61, 0x7A, 0x65, 0x20, 0x6F, 0x6E, 0x20, 0x68, 0x69,
0x6D, 0x0A, 0x57, 0x68, 0x65, 0x6E, 0x20, 0x68, 0x65, 0x20, 0x62, 0x65, 0x73, 0x74, 0x72, 0x69,
0x64, 0x65, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6C, 0x61, 0x7A, 0x79, 0x2D, 0x70, 0x61, 0x63,
0x69, 0x6E, 0x67, 0x20, 0x63, 0x6C, 0x6F, 0x75, 0x64, 0x73, 0x0A, 0x41, 0x6E, 0x64, 0x20, 0x73,
0x61, 0x69, 0x6C, 0x73, 0x20, 0x75, 0x70, 0x6F, 0x6E, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x6F,
0x73, 0x6F, 0x6D, 0x20, 0x6F, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x69, 0x72, 0x2E, 0x0A,
0x0A, 0x4A, 0x75, 0x6C, 0x2E, 0x20, 0x4F, 0x20, 0x52, 0x6F, 0x6D, 0x65, 0x6F, 0x2C, 0x20, 0x52,
0x6F, 0x6D, 0x65, 0x6F, 0x21, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x20,
0x61, 0x72, 0x74, 0x20, 0x74, 0x68, 0x6F, 0x75, 0x20, 0x52, 0x6F, 0x6D, 0x65, 0x6F, 0x3F, 0x0A,
0x44, 0x65, 0x6E, 0x79, 0x20, 0x74, 0x68, 0x79, 0x20, 0x66, 0x61, 0x74, 0x68, 0x65, 0x72, 0x20,
0x61, 0x6E, 0x64, 0x20, 0x72, 0x65, 0x66, 0x75, 0x73, 0x65, 0x20, 0x74, 0x68, 0x79, 0x20, 0x6E,
0x61, 0x6D, 0x65, 0x21, 0x0A, 0x4F, 0x72, 0x2C, 0x20, 0x69, 0x66, 0x20, 0x74, 0x68, 0x6F, 0x75,
0x20, 0x77, 0x69, 0x6C, 0x74, 0x20, 0x6E, 0x6F, 0x74, 0x2C, 0x20, 0x62, 0x65, 0x20, 0x62, 0x75,
0x74, 0x20, 0x73, 0x77, 0x6F, 0x72, 0x6E, 0x20, 0x6D, 0x79, 0x20, 0x6C, 0x6F, 0x76, 0x65, 0x2C,
0x0A, 0x41, 0x6E, 0x64, 0x20, 0x49, 0x27, 0x6C, 0x6C, 0x20, 0x6E, 0x6F, 0x20, 0x6C, 0x6F, 0x6E,
0x67, 0x65, 0x72, 0x20, 0x62, 0x65, 0x20, 0x61, 0x20, 0x43, 0x61, 0x70, 0x75, 0x6C, 0x65, 0x74
},
// Set signature to 0xAA55 to make drive bootable
/*uint16_t*/.signature = 0x0000,
#endif
};
enum virtual_media_idx_t
{
MEDIA_IDX_MBR = 0,
MEDIA_IDX_FAT1,
MEDIA_IDX_FAT2,
MEDIA_IDX_ROOT_DIR,
MEDIA_IDX_COUNT
};
// Note - everything in virtual media must be a multiple of VFS_SECTOR_SIZE
const virtual_media_t virtual_media_tmpl[] = {
/* Read CB Write CB Region Size Region Name */
{read_mbr, write_none, VFS_SECTOR_SIZE}, /* MBR */
{read_fat, write_none, 0 /* Set at runtime */ }, /* FAT1 */
{read_fat, write_none, 0 /* Set at runtime */ }, /* FAT2 */
{read_dir, write_dir, VFS_SECTOR_SIZE * 2}, /* Root Dir */
/* Raw filesystem contents follow */
};
// Keep virtual_media_idx_t in sync with virtual_media_tmpl
COMPILER_ASSERT(MEDIA_IDX_COUNT == ELEMENTS_IN_ARRAY(virtual_media_tmpl));
#define DOS_DATE(y, m, d) ((((y)-1980)<<9)|((m)<<5)|(d))
#define DOS_TIME(h, m, s) (((h)<<11)|((m)<<5)|((s)>>1))
static const FatDirectoryEntry_t root_dir_entry = {
/*uint8_t[11] */ .filename = {""},
/*uint8_t */ .attributes = VFS_FILE_ATTR_VOLUME_LABEL | VFS_FILE_ATTR_ARCHIVE,
/*uint8_t */ .reserved = 0x00,
/*uint8_t */ .creation_time_ms = 0x00,
/*uint16_t*/ .creation_time = 0x0000,
/*uint16_t*/ .creation_date = 0x0000,
/*uint16_t*/ .accessed_date = 0x0000,
/*uint16_t*/ .first_cluster_high_16 = 0x0000,
/*uint16_t*/ .modification_time = 0x8E41, // nobody will ever see this
/*uint16_t*/ .modification_date = 0x32bb,
/*uint16_t*/ .first_cluster_low_16 = 0x0000,
/*uint32_t*/ .filesize = 0x00000000
};
static const FatDirectoryEntry_t dir_entry_tmpl = {
/*uint8_t[11] */ .filename = {""},
/*uint8_t */ .attributes = 0, //VFS_FILE_ATTR_READ_ONLY
/*uint8_t */ .reserved = 0x00,
/*uint8_t */ .creation_time_ms = 0x00,
/*uint16_t*/ .creation_time = 0x0000,
/*uint16_t*/ .creation_date = 0x4876,
/*uint16_t*/ .accessed_date = 0x4876,
/*uint16_t*/ .first_cluster_high_16 = 0x0000,
/*uint16_t*/ .modification_time = DOS_TIME(0, 0, 0),
/*uint16_t*/ .modification_date = DOS_DATE(1984, 4, 4),
/*uint16_t*/ .first_cluster_low_16 = 0x0000,
/*uint32_t*/ .filesize = 0x00000000
};
//mbr_t mbr;
file_allocation_table_t fat;
virtual_media_t virtual_media[VFS_MAX_FILES];
union {
FatDirectoryEntry_t initial[VFS_MAX_FILES];
root_dir_t current;
} rootdir;
#define dir_current rootdir.current
#define dir_initial rootdir.initial
uint8_t file_count;
vfs_file_change_cb_t file_change_cb;
uint32_t virtual_media_idx;
uint32_t fat_idx;
uint32_t dir_idx;
uint32_t data_start;
bool init_complete;
// Virtual media must be larger than the template
COMPILER_ASSERT(sizeof(virtual_media) > sizeof(virtual_media_tmpl));
static void write_fat(file_allocation_table_t *aFat, uint32_t idx, uint16_t val)
{
uint32_t low_idx;
uint32_t high_idx;
low_idx = idx * 2 + 0;
high_idx = idx * 2 + 1;
// Assert that this is still within the fat table
if (high_idx >= ELEMENTS_IN_ARRAY(aFat->f)) {
assert_param(0);
return;
}
aFat->f[low_idx] = (val >> 0) & 0xFF;
aFat->f[high_idx] = (val >> 8) & 0xFF;
}
void vfs_init(const vfs_filename_t drive_name, uint32_t disk_size)
{
uint32_t i;
// uint32_t num_clusters;
// uint32_t total_sectors;
// Clear everything
// memset(&mbr, 0, sizeof(mbr));
memset(&fat, 0, sizeof(fat));
fat_idx = 0;
memset(&virtual_media, 0, sizeof(virtual_media));
memset(&dir_current, 0, sizeof(dir_current));
// memset(&dir_initial, 0, sizeof(dir_initial));
dir_idx = 0;
file_count = 0;
file_change_cb = file_change_cb_stub;
virtual_media_idx = 0;
data_start = 0;
init_complete = false;
//
// vfs_printf("MBR totalsec %d, min %d, max %d", mbr.total_logical_sectors,
// FAT_CLUSTERS_MIN * mbr.sectors_per_cluster,
// FAT_CLUSTERS_MAX * mbr.sectors_per_cluster);
//
// MIN(MAX(, FAT_CLUSTERS_MIN * mbr.sectors_per_cluster), FAT_CLUSTERS_MAX * mbr.sectors_per_cluster)
// // Initialize MBR
//// memcpy(&mbr, &mbr_tmpl, sizeof(mbr_t));
// total_sectors = ((disk_size + KB(64)) / mbr.bytes_per_sector);
// // Make sure this is the right size for a FAT16 volume
// if (total_sectors < FAT_CLUSTERS_MIN * mbr.sectors_per_cluster) {
// assert_param(0);
// total_sectors = FAT_CLUSTERS_MIN * mbr.sectors_per_cluster;
// } else if (total_sectors > FAT_CLUSTERS_MAX * mbr.sectors_per_cluster) {
// assert_param(0);
// total_sectors = FAT_CLUSTERS_MAX * mbr.sectors_per_cluster;
// }
// if (total_sectors >= 0x10000) {
// mbr.total_logical_sectors = 0;
// mbr.big_sectors_on_drive = total_sectors;
// } else {
// mbr.total_logical_sectors = total_sectors;
// mbr.big_sectors_on_drive = 0;
// }
// // FAT table will likely be larger than needed, but this is allowed by the
// // fat specification
// num_clusters = total_sectors / mbr.sectors_per_cluster;
// mbr.logical_sectors_per_fat = (num_clusters * 2 + VFS_SECTOR_SIZE - 1) / VFS_SECTOR_SIZE;
// Initailize virtual media
memcpy(&virtual_media, &virtual_media_tmpl, sizeof(virtual_media_tmpl));
virtual_media[MEDIA_IDX_FAT1].length = VFS_SECTOR_SIZE * mbr.logical_sectors_per_fat;
virtual_media[MEDIA_IDX_FAT2].length = VFS_SECTOR_SIZE * mbr.logical_sectors_per_fat;
// Initialize indexes
virtual_media_idx = MEDIA_IDX_COUNT;
data_start = 0;
for (i = 0; i < ELEMENTS_IN_ARRAY(virtual_media_tmpl); i++) {
data_start += virtual_media[i].length;
}
// Initialize FAT
fat_idx = 0;
write_fat(&fat, fat_idx, 0xFFF8); // Media type "media_descriptor"
fat_idx++;
write_fat(&fat, fat_idx, 0xFFFF); // FAT12 - always 0xFFF (no meaning), FAT16 - dirty/clean (clean = 0xFFFF)
fat_idx++;
// Initialize root dir
dir_idx = 0;
dir_current.f[dir_idx] = root_dir_entry;
memcpy(dir_current.f[dir_idx].filename, drive_name, sizeof(dir_current.f[0].filename));
dir_idx++;
}
uint32_t vfs_get_total_size(void)
{
uint32_t size;
if (mbr.total_logical_sectors > 0) {
size = mbr.total_logical_sectors * mbr.bytes_per_sector;
} else if (mbr.big_sectors_on_drive > 0) {
size = mbr.big_sectors_on_drive * mbr.bytes_per_sector;
} else {
size = 0;
assert_param(0);
}
return size;
}
vfs_file_t vfs_create_file(const vfs_filename_t filename, vfs_read_cb_t read_cb, vfs_write_cb_t write_cb, uint32_t len)
{
uint32_t first_cluster;
FatDirectoryEntry_t *de;
uint32_t clusters;
uint32_t cluster_size;
uint32_t i;
assert_param(filename_valid(filename));
// Compute the number of clusters in the file
cluster_size = mbr.bytes_per_sector * mbr.sectors_per_cluster;
clusters = (len + cluster_size - 1) / cluster_size;
// Write the cluster chain to the fat table
first_cluster = 0;
if (len > 0) {
first_cluster = fat_idx;
for (i = 0; i < clusters - 1; i++) {
write_fat(&fat, fat_idx, fat_idx + 1);
fat_idx++;
}
write_fat(&fat, fat_idx, 0xFFFF);
fat_idx++;
}
// Update directory entry
if (dir_idx >= ELEMENTS_IN_ARRAY(dir_current.f)) {
assert_param(0);
return VFS_FILE_INVALID;
}
de = &dir_current.f[dir_idx];
dir_idx++;
memcpy(de, &dir_entry_tmpl, sizeof(dir_entry_tmpl));
memcpy(de->filename, filename, 11);
de->filesize = len;
de->first_cluster_high_16 = (first_cluster >> 16) & 0xFFFF;
de->first_cluster_low_16 = (first_cluster >> 0) & 0xFFFF;
// Update virtual media
if (virtual_media_idx >= ELEMENTS_IN_ARRAY(virtual_media)) {
assert_param(0);
return VFS_FILE_INVALID;
}
virtual_media[virtual_media_idx].read_cb = read_zero;
virtual_media[virtual_media_idx].write_cb = write_none;
if (0 != read_cb) {
virtual_media[virtual_media_idx].read_cb = read_cb;
}
if (0 != write_cb) {
virtual_media[virtual_media_idx].write_cb = write_cb;
}
virtual_media[virtual_media_idx].length = clusters * mbr.bytes_per_sector * mbr.sectors_per_cluster;
virtual_media_idx++;
file_count += 1;
return de;
}
void vfs_file_set_attr(vfs_file_t file, vfs_file_attr_bit_t attr)
{
FatDirectoryEntry_t *de = file;
de->attributes = attr;
}
vfs_sector_t vfs_file_get_start_sector(vfs_file_t file)
{
FatDirectoryEntry_t *de = file;
if (vfs_file_get_size(file) == 0) {
return VFS_INVALID_SECTOR;
}
return cluster_to_sector(de->first_cluster_low_16);
}
uint32_t vfs_file_get_size(vfs_file_t file)
{
FatDirectoryEntry_t *de = file;
return de->filesize;
}
vfs_file_attr_bit_t vfs_file_get_attr(vfs_file_t file)
{
FatDirectoryEntry_t *de = file;
return (vfs_file_attr_bit_t) de->attributes;
}
void vfs_set_file_change_callback(vfs_file_change_cb_t cb)
{
file_change_cb = cb;
}
void vfs_read(uint32_t requested_sector, uint8_t *buf, uint32_t num_sectors)
{
uint8_t i = 0;
uint32_t current_sector;
// Zero out the buffer
memset(buf, 0, num_sectors * VFS_SECTOR_SIZE);
current_sector = 0;
set_init_done();
// vfs_printf("vfs_read sec %d, len %d secs", requested_sector, num_sectors);
for (i = 0; i < ELEMENTS_IN_ARRAY(virtual_media); i++) {
uint32_t vm_sectors = virtual_media[i].length / VFS_SECTOR_SIZE;
uint32_t vm_start = current_sector;
uint32_t vm_end = current_sector + vm_sectors;
// Data can be used in this sector
if ((requested_sector >= vm_start) && (requested_sector < vm_end)) {
uint32_t sector_offset;
uint32_t sectors_to_write = vm_end - requested_sector;
sectors_to_write = MIN(sectors_to_write, num_sectors);
sector_offset = requested_sector - current_sector;
virtual_media[i].read_cb(sector_offset, buf, sectors_to_write);
// Update requested sector
requested_sector += sectors_to_write;
num_sectors -= sectors_to_write;
}
// If there is no more data to be read then break
if (num_sectors == 0) {
break;
}
// Move to the next virtual media entry
current_sector += vm_sectors;
}
}
void vfs_write(uint32_t requested_sector, const uint8_t *buf, uint32_t num_sectors)
{
uint8_t i = 0;
uint32_t current_sector;
current_sector = 0;
set_init_done();
vfs_printf("vfs_write - at sector %d, count %d", (int)requested_sector, (int)num_sectors);
for (i = 0; i < virtual_media_idx; i++) {
uint32_t vm_sectors = virtual_media[i].length / VFS_SECTOR_SIZE;
uint32_t vm_start = current_sector;
uint32_t vm_end = current_sector + vm_sectors;
//vfs_printf("Testing file %d: (%d -> %d)", i, vm_start, vm_end);
// Data can be used in this sector
if ((requested_sector >= vm_start) && (requested_sector < vm_end)) {
uint32_t sector_offset;
uint32_t sectors_to_read = vm_end - requested_sector;
sectors_to_read = MIN(sectors_to_read, num_sectors);
sector_offset = requested_sector - current_sector;
virtual_media[i].write_cb(sector_offset, buf, sectors_to_read);
// Update requested sector
requested_sector += sectors_to_read;
num_sectors -= sectors_to_read;
}
// If there is no more data to be read then break
if (num_sectors == 0) {
break;
}
// Move to the next virtual media entry
current_sector += vm_sectors;
}
if (num_sectors > 0) vfs_printf("Failed to find place for writing, remain %d secs to write.", (int)num_sectors);
}
static uint32_t read_zero(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t read_size = VFS_SECTOR_SIZE * num_sectors;
memset(data, 0, read_size);
return read_size;
}
static void write_none(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
{
// Do nothing
}
static uint32_t read_mbr(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t read_size = VFS_SECTOR_SIZE;
if (sector_offset != 0) {
// Don't worry about reading other sectors
return 0;
}
// clear the buffer - MBR is not complete
memset(data, 0, read_size);
// copy MBR
memcpy(data, &mbr, sizeof(mbr_t));
return VFS_SECTOR_SIZE;
}
/* No need to handle writes to the mbr */
static uint32_t read_fat(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t read_size = sizeof(file_allocation_table_t);
COMPILER_ASSERT(sizeof(file_allocation_table_t) <= VFS_SECTOR_SIZE);
if (sector_offset != 0) {
// Don't worry about reading other sectors
return 0;
}
memcpy(data, &fat, read_size);
return read_size;
}
/* No need to handle writes to the fat */
static uint32_t read_dir(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
{
uint32_t start_index;
uint32_t copy_size;
if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) {
// Trying to read too much of the root directory
assert_param(0);
return 0;
}
// Zero buffer
memset(data, 0, num_sectors * VFS_SECTOR_SIZE);
start_index = sector_offset * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
// Copy data if anything can be copied
if (start_index < ELEMENTS_IN_ARRAY(dir_initial)) {
assert_param(sizeof(dir_initial) > sector_offset * VFS_SECTOR_SIZE);
copy_size = sizeof(dir_initial) - sector_offset * VFS_SECTOR_SIZE;
memcpy(data, &dir_initial[start_index], copy_size);
}
return num_sectors * VFS_SECTOR_SIZE;
}
static void write_dir(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
{
FatDirectoryEntry_t *old_entry;
FatDirectoryEntry_t *new_entry;
uint32_t start_index;
uint32_t num_entries;
uint32_t i;
if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) {
// Trying to write too much of the root directory
assert_param(0);
return;
}
start_index = sector_offset * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
num_entries = num_sectors * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
old_entry = &dir_current.f[start_index];
new_entry = (FatDirectoryEntry_t *) data;
// If this is the first sector start at index 1 to get past drive name
i = 0 == sector_offset ? 1 : 0;
for (; i < num_entries; i++) {
bool same_name;
if (0 == memcmp(&old_entry[i], &new_entry[i], sizeof(FatDirectoryEntry_t))) {
continue;
}
// If were at this point then something has changed in the file
same_name = (0 == memcmp(old_entry[i].filename, new_entry[i].filename, sizeof(new_entry[i].filename))) ? 1 : 0;
// Changed
if (new_entry[i].attributes != VFS_FILE_ATTR_LFN) {
file_change_cb(new_entry[i].filename, VFS_FILE_CHANGED, (vfs_file_t) &old_entry[i],
(vfs_file_t) &new_entry[i]);
}
// Deleted
if (old_entry[i].attributes != VFS_FILE_ATTR_LFN && 0xe5 == (uint8_t) new_entry[i].filename[0]) {
file_change_cb(old_entry[i].filename, VFS_FILE_DELETED, (vfs_file_t) &old_entry[i], (vfs_file_t) &new_entry[i]);
continue;
}
// Created
if (new_entry[i].attributes != VFS_FILE_ATTR_LFN && !same_name && filename_valid(new_entry[i].filename)) {
file_change_cb(new_entry[i].filename, VFS_FILE_CREATED, (vfs_file_t) &old_entry[i], (vfs_file_t) &new_entry[i]);
continue;
}
}
memcpy(&dir_current.f[start_index], data, num_sectors * VFS_SECTOR_SIZE);
}
static void file_change_cb_stub(const vfs_filename_t filename, vfs_file_change_t change, vfs_file_t file, vfs_file_t new_file_data)
{
// Do nothing
}
static uint32_t cluster_to_sector(uint32_t cluster_idx)
{
uint32_t sectors_before_data = data_start / mbr.bytes_per_sector;
return sectors_before_data + (cluster_idx - 2) * mbr.sectors_per_cluster;
}
static bool filename_valid(const vfs_filename_t filename)
{
// Information on valid 8.3 filenames can be found in
// the microsoft hardware whitepaper:
//
// Microsoft Extensible Firmware Initiative
// FAT32 File System Specification
// FAT: General Overview of On-Disk Format
const char invalid_starting_chars[] = {
0xE5, // Deleted
0x00, // Deleted (and all following entries are free)
0x20, // Space not allowed as first character
};
uint32_t i;
// Check for invalid starting characters
for (i = 0; i < sizeof(invalid_starting_chars); i++) {
if (invalid_starting_chars[i] == filename[0]) {
return false;
}
}
// Make sure all the characters are valid
for (i = 0; i < sizeof(vfs_filename_t); i++) {
if (!filename_character_valid(filename[i])) {
return false;
}
}
// All checks have passed so filename is valid
return true;
}
static bool filename_character_valid(char character)
{
const char invalid_chars[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
uint32_t i;
// Lower case characters are not allowed
if ((character >= 'a') && (character <= 'z')) {
return false;
}
// Values less than 0x20 are not allowed except 0x5
if ((character < 0x20) && (character != 0x5)) {
return false;
}
// Check for special characters that are not allowed
for (i = 0; i < sizeof(invalid_chars); i++) {
if (invalid_chars[i] == character) {
return false;
}
}
// All of the checks have passed so this is a valid file name character
return true;
}
static void set_init_done(void)
{
if (!init_complete) {
// memcpy(&dir_initial, &dir_current, MIN(sizeof(dir_initial), sizeof(dir_current)));
init_complete = true;
}
}
bool vfs_find_file(uint32_t start_sector, vfs_filename_t *destFilename, vfs_file_t **destFile)
{
vfs_printf("Looking for file at %d", (int)start_sector);
for (int i = 0; i < 32; i++) {
FatDirectoryEntry_t *f = &dir_current.f[i];
if (f->attributes == VFS_FILE_ATTR_LFN) continue;
if (cluster_to_sector((f->first_cluster_high_16 << 16) + f->first_cluster_low_16) == start_sector) {
memcpy(destFilename, f->filename, sizeof(vfs_filename_t));
vfs_printf("Found one at: %s - SUCCESS!!", f->filename);
*destFile = (vfs_file_t *) f;
return true;
}
}
vfs_printf("NOT FOUND.");
return false;
}