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.
804 lines
29 KiB
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;
|
|
}
|
|
|