Repostiory containing DAPLink source code with Reset Pin workaround for HANI_IOT board.
Upstream: https://github.com/ARMmbed/DAPLink
Diff: source/daplink/drag-n-drop/virtual_fs.c
- Revision:
- 0:01f31e923fe2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/daplink/drag-n-drop/virtual_fs.c Tue Apr 07 12:55:42 2020 +0200 @@ -0,0 +1,744 @@ +/** + * @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 <string.h> + +#include "virtual_fs.h" +#include "info.h" +#include "settings.h" +#include "compiler.h" +#include "util.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) + +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]; + /* 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; +} __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[32]; +} 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); + +// 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)); +static const mbr_t mbr_tmpl = { + /*uint8_t[11]*/.boot_sector = { + 0xEB, 0x3C, 0x90, + 'M', 'S', 'D', '0', 'S', '4', '.', '1' // OEM Name in text (8 chars max) + }, + /*uint16_t*/.bytes_per_sector = 0x0200, // 512 bytes per sector + /*uint8_t */.sectors_per_cluster = 0x08, // 4k cluser + /*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 = 0x1f50, // sector size * # of sectors = drive size + /*uint8_t */.media_descriptor = 0xf8, // fixed disc = F8, removable = F0 + /*uint16_t*/.logical_sectors_per_fat = 0x0001, // FAT is 1k - ToDO:need to edit this + /*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 = 0x00000000, // 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 + /*char[11]*/.volume_label = {'D', 'A', 'P', 'L', 'I', 'N', 'K', '-', 'D', 'N', 'D'}, + // unused by msft - just a label (FAT, FAT12, FAT16) + /*char[8] */.file_system_type = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '}, + + /* BOOTSTRAP SOURCE CODE AND PAYLOAD GENERATOR + * PRINTS OUT WARNING MESSAGE ON ACCIDENTAL BOOT FROM DAPLINK + 1 [BITS 16] + 2 %define BLSTART 0x3E + 3 %define BLLEN 448 + 4 + 5 00000000 FA cli + 6 00000001 B8C007 mov ax, 07C0h + 7 00000004 052001 add ax, 288 + 8 00000007 8ED0 mov ss, ax + 9 00000009 BC0010 mov sp, 4096 + 10 0000000C B8C007 mov ax, 07C0h + 11 0000000F 8ED8 mov ds, ax + 12 00000011 BE[6D00] mov si,message+BLSTART + 13 00000014 E80B00 call print + 14 00000017 EBFE jmp $ + 15 + 16 printc: + 17 00000019 B40E mov ah, 0x0E + 18 0000001B B700 mov bh, 0x00 + 19 0000001D B307 mov bl, 0x07 + 20 0000001F CD10 int 0x10 + 21 00000021 C3 ret + 22 + 23 print: + 24 nextc: + 25 00000022 8A04 mov al, [si] + 26 00000024 46 inc si + 27 00000025 08C0 or al, al + 28 00000027 7405 jz return + 29 00000029 E8EDFF call printc + 30 0000002C EBF4 jmp nextc + 31 return: + 32 0000002E C3 ret + 33 + 34 0000002F 504C45415345205245- message db 'PLEASE REMOVE THE ARM MBED DAPLINK USB DEVICE AND REBOOT THE SYSTEM..', 0 + 35 00000038 4D4F56452054484520- + 36 00000041 41524D204D42454420- + 37 0000004A 4441504C494E4B2055- + 38 00000053 534220444556494345- + 39 0000005C 20414E44205245424F- + 40 00000065 4F5420544845205359- + 41 0000006E 5354454D2E2E00 + 42 + 43 00000075 00<rept> times BLLEN-($-$$) db 0 + + USE BELOW SCRIPT TO COMPILE BOOTSTRAP AND GENERATE PAYLOAD: + #!/usr/bin/env python + import os + os.system('nasm -f bin -o print.bin -l print.lst print.asm') + print(open('print.lst','r').read()) + x=1 + for c in open('print.bin','rb').read(): + print('0x%02X, '%c, end='' if x % 16 else '\n') + x += 1 + */ + /*uint8_t[448]*/.bootstrap = { + 0xFA, 0xB8, 0xC0, 0x07, 0x05, 0x20, 0x01, 0x8E, 0xD0, 0xBC, 0x00, 0x10, 0xB8, 0xC0, 0x07, 0x8E, + 0xD8, 0xBE, 0x6D, 0x00, 0xE8, 0x0B, 0x00, 0xEB, 0xFE, 0xB4, 0x0E, 0xB7, 0x00, 0xB3, 0x07, 0xCD, + 0x10, 0xC3, 0x8A, 0x04, 0x46, 0x08, 0xC0, 0x74, 0x05, 0xE8, 0xED, 0xFF, 0xEB, 0xF4, 0xC3, 0x50, + 0x4C, 0x45, 0x41, 0x53, 0x45, 0x20, 0x52, 0x45, 0x4D, 0x4F, 0x56, 0x45, 0x20, 0x54, 0x48, 0x45, + 0x20, 0x41, 0x52, 0x4D, 0x20, 0x4D, 0x42, 0x45, 0x44, 0x20, 0x44, 0x41, 0x50, 0x4C, 0x49, 0x4E, + 0x4B, 0x20, 0x55, 0x53, 0x42, 0x20, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x20, 0x41, 0x4E, 0x44, + 0x20, 0x52, 0x45, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x59, 0x53, 0x54, + 0x45, 0x4D, 0x2E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + // Signature MUST be 0xAA55 to maintain compatibility (i.e. with Android). + /*uint16_t*/.signature = 0xAA55, +}; + +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 == ARRAY_SIZE(virtual_media_tmpl)); + +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, + /*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 = 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 = 0x83dc, + /*uint16_t*/ .modification_date = 0x4876, + /*uint16_t*/ .first_cluster_low_16 = 0x0000, + /*uint32_t*/ .filesize = 0x00000000 +}; + +mbr_t mbr; +file_allocation_table_t fat; +virtual_media_t virtual_media[16]; +root_dir_t dir_current; +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; + +// 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 *fat, 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 >= ARRAY_SIZE(fat->f)) { + util_assert(0); + return; + } + + fat->f[low_idx] = (val >> 0) & 0xFF; + fat->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)); + dir_idx = 0; + file_count = 0; + file_change_cb = file_change_cb_stub; + virtual_media_idx = 0; + data_start = 0; + // 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) { + util_assert(0); + total_sectors = FAT_CLUSTERS_MIN * mbr.sectors_per_cluster; + } else if (total_sectors > FAT_CLUSTERS_MAX * mbr.sectors_per_cluster) { + util_assert(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 < ARRAY_SIZE(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() +{ + 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; + util_assert(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; + util_assert(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 >= ARRAY_SIZE(dir_current.f)) { + util_assert(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 >= ARRAY_SIZE(virtual_media)) { + util_assert(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; + + for (i = 0; i < ARRAY_SIZE(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; + + 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; + + // 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; + } +} + +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 = sizeof(mbr_t); + COMPILER_ASSERT(sizeof(mbr_t) <= VFS_SECTOR_SIZE); + + if (sector_offset != 0) { + // Don't worry about reading other sectors + return 0; + } + + memcpy(data, &mbr, read_size); + return read_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) +{ + if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) { + // Trying to read too much of the root directory + util_assert(0); + return 0; + } + + // Zero buffer data is VFS_SECTOR_SIZE max + memset(data, 0, VFS_SECTOR_SIZE); + + if (sector_offset == 0) { //Handle the first 512 bytes + // Copy data that is actually created in the directory + memcpy(data, &dir_current.f[0], dir_idx*sizeof(FatDirectoryEntry_t)); + } + + 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 + util_assert(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 + file_change_cb(new_entry[i].filename, VFS_FILE_CHANGED, (vfs_file_t)&old_entry[i], (vfs_file_t)&new_entry[i]); + + // Deleted + if (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 (!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; +}