Arrow / Mbed OS DAPLink Reset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers virtual_fs.c Source File

virtual_fs.c

Go to the documentation of this file.
00001 /**
00002  * @file    virtual_fs.c
00003  * @brief   Implementation of virtual_fs.h
00004  *
00005  * DAPLink Interface Firmware
00006  * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
00007  * SPDX-License-Identifier: Apache-2.0
00008  *
00009  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00010  * not use this file except in compliance with the License.
00011  * You may obtain a copy of the License at
00012  *
00013  * http://www.apache.org/licenses/LICENSE-2.0
00014  *
00015  * Unless required by applicable law or agreed to in writing, software
00016  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00017  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00018  * See the License for the specific language governing permissions and
00019  * limitations under the License.
00020  */
00021 
00022 #include <string.h>
00023 
00024 #include "virtual_fs.h"
00025 #include "info.h"
00026 #include "settings.h"
00027 #include "compiler.h"
00028 #include "util.h"
00029 
00030 // Virtual file system driver
00031 // Limitations:
00032 //   - files must be contiguous
00033 //   - data written cannot be read back
00034 //   - data should only be read once
00035 
00036 // FAT16 limitations +- safety margin
00037 #define FAT_CLUSTERS_MAX (65525 - 100)
00038 #define FAT_CLUSTERS_MIN (4086 + 100)
00039 
00040 typedef struct {
00041     uint8_t boot_sector[11];
00042     /* DOS 2.0 BPB - Bios Parameter Block, 11 bytes */
00043     uint16_t bytes_per_sector;
00044     uint8_t  sectors_per_cluster;
00045     uint16_t reserved_logical_sectors;
00046     uint8_t  num_fats;
00047     uint16_t max_root_dir_entries;
00048     uint16_t total_logical_sectors;
00049     uint8_t  media_descriptor;
00050     uint16_t logical_sectors_per_fat;
00051     /* DOS 3.31 BPB - Bios Parameter Block, 12 bytes */
00052     uint16_t physical_sectors_per_track;
00053     uint16_t heads;
00054     uint32_t hidden_sectors;
00055     uint32_t big_sectors_on_drive;
00056     /* Extended BIOS Parameter Block, 26 bytes */
00057     uint8_t  physical_drive_number;
00058     uint8_t  not_used;
00059     uint8_t  boot_record_signature;
00060     uint32_t volume_id;
00061     char     volume_label[11];
00062     char     file_system_type[8];
00063     /* bootstrap data in bytes 62-509 */
00064     uint8_t  bootstrap[448];
00065     /* These entries in place of bootstrap code are the *nix partitions */
00066     //uint8_t  partition_one[16];
00067     //uint8_t  partition_two[16];
00068     //uint8_t  partition_three[16];
00069     //uint8_t  partition_four[16];
00070     /* Mandatory value at bytes 510-511, must be 0xaa55 */
00071     uint16_t signature;
00072 } __attribute__((packed)) mbr_t;
00073 
00074 typedef struct file_allocation_table {
00075     uint8_t f[512];
00076 } file_allocation_table_t;
00077 
00078 typedef struct FatDirectoryEntry {
00079     vfs_filename_t filename;
00080     uint8_t attributes;
00081     uint8_t reserved;
00082     uint8_t creation_time_ms;
00083     uint16_t creation_time;
00084     uint16_t creation_date;
00085     uint16_t accessed_date;
00086     uint16_t first_cluster_high_16;
00087     uint16_t modification_time;
00088     uint16_t modification_date;
00089     uint16_t first_cluster_low_16;
00090     uint32_t filesize;
00091 } __attribute__((packed)) FatDirectoryEntry_t;
00092 COMPILER_ASSERT(sizeof(FatDirectoryEntry_t) == 32);
00093 
00094 // to save RAM all files must be in the first root dir entry (512 bytes)
00095 //  but 2 actually exist on disc (32 entries) to accomodate hidden OS files,
00096 //  folders and metadata
00097 typedef struct root_dir {
00098     FatDirectoryEntry_t f[32];
00099 } root_dir_t;
00100 
00101 typedef struct virtual_media {
00102     vfs_read_cb_t read_cb;
00103     vfs_write_cb_t write_cb;
00104     uint32_t length;
00105 } virtual_media_t;
00106 
00107 static uint32_t read_zero(uint32_t offset, uint8_t *data, uint32_t size);
00108 static void write_none(uint32_t offset, const uint8_t *data, uint32_t size);
00109 
00110 static uint32_t read_mbr(uint32_t offset, uint8_t *data, uint32_t size);
00111 static uint32_t read_fat(uint32_t offset, uint8_t *data, uint32_t size);
00112 static uint32_t read_dir(uint32_t offset, uint8_t *data, uint32_t size);
00113 static void write_dir(uint32_t offset, const uint8_t *data, uint32_t size);
00114 static void file_change_cb_stub(const vfs_filename_t filename, vfs_file_change_t  change,
00115                                 vfs_file_t file, vfs_file_t new_file_data);
00116 static uint32_t cluster_to_sector(uint32_t cluster_idx);
00117 static bool filename_valid(const vfs_filename_t filename);
00118 static bool filename_character_valid(char character);
00119 
00120 // If sector size changes update comment below
00121 COMPILER_ASSERT(0x0200 == VFS_SECTOR_SIZE);
00122 // If root directory size changes update max_root_dir_entries
00123 COMPILER_ASSERT(0x0020 == sizeof(root_dir_t) / sizeof(FatDirectoryEntry_t));
00124 static const mbr_t mbr_tmpl = {
00125     /*uint8_t[11]*/.boot_sector = {
00126         0xEB, 0x3C, 0x90,
00127         'M', 'S', 'D', '0', 'S', '4', '.', '1' // OEM Name in text (8 chars max)
00128     },
00129     /*uint16_t*/.bytes_per_sector           = 0x0200,       // 512 bytes per sector
00130     /*uint8_t */.sectors_per_cluster        = 0x08,         // 4k cluser
00131     /*uint16_t*/.reserved_logical_sectors   = 0x0001,       // mbr is 1 sector
00132     /*uint8_t */.num_fats                   = 0x02,         // 2 FATs
00133     /*uint16_t*/.max_root_dir_entries       = 0x0020,       // 32 dir entries (max)
00134     /*uint16_t*/.total_logical_sectors      = 0x1f50,       // sector size * # of sectors = drive size
00135     /*uint8_t */.media_descriptor           = 0xf8,         // fixed disc = F8, removable = F0
00136     /*uint16_t*/.logical_sectors_per_fat    = 0x0001,       // FAT is 1k - ToDO:need to edit this
00137     /*uint16_t*/.physical_sectors_per_track = 0x0001,       // flat
00138     /*uint16_t*/.heads                      = 0x0001,       // flat
00139     /*uint32_t*/.hidden_sectors             = 0x00000000,   // before mbt, 0
00140     /*uint32_t*/.big_sectors_on_drive       = 0x00000000,   // 4k sector. not using large clusters
00141     /*uint8_t */.physical_drive_number      = 0x00,
00142     /*uint8_t */.not_used                   = 0x00,         // Current head. Linux tries to set this to 0x1
00143     /*uint8_t */.boot_record_signature      = 0x29,         // signature is present
00144     /*uint32_t*/.volume_id                  = 0x27021974,   // serial number
00145     // needs to match the root dir label
00146     /*char[11]*/.volume_label               = {'D', 'A', 'P', 'L', 'I', 'N', 'K', '-', 'D', 'N', 'D'},
00147     // unused by msft - just a label (FAT, FAT12, FAT16)
00148     /*char[8] */.file_system_type           = {'F', 'A', 'T', '1', '6', ' ', ' ', ' '},
00149 
00150     /* BOOTSTRAP SOURCE CODE AND PAYLOAD GENERATOR
00151      * PRINTS OUT WARNING MESSAGE ON ACCIDENTAL BOOT FROM DAPLINK
00152      1                                  [BITS 16]
00153      2                                  %define BLSTART 0x3E
00154      3                                  %define BLLEN 448
00155      4
00156      5 00000000 FA                      cli
00157      6 00000001 B8C007                  mov ax, 07C0h
00158      7 00000004 052001                  add ax, 288
00159      8 00000007 8ED0                    mov ss, ax
00160      9 00000009 BC0010                  mov sp, 4096
00161     10 0000000C B8C007                  mov ax, 07C0h
00162     11 0000000F 8ED8                    mov ds, ax
00163     12 00000011 BE[6D00]                mov si,message+BLSTART
00164     13 00000014 E80B00                  call print
00165     14 00000017 EBFE                    jmp $
00166     15
00167     16                                  printc:
00168     17 00000019 B40E                        mov ah, 0x0E
00169     18 0000001B B700                        mov bh, 0x00
00170     19 0000001D B307                        mov bl, 0x07
00171     20 0000001F CD10                        int 0x10
00172     21 00000021 C3                          ret
00173     22
00174     23                                  print:
00175     24                                      nextc:
00176     25 00000022 8A04                            mov al, [si]
00177     26 00000024 46                              inc si
00178     27 00000025 08C0                            or al, al
00179     28 00000027 7405                            jz return
00180     29 00000029 E8EDFF                          call printc
00181     30 0000002C EBF4                            jmp nextc
00182     31                                      return:
00183     32 0000002E C3                              ret
00184     33
00185     34 0000002F 504C45415345205245-     message db 'PLEASE REMOVE THE ARM MBED DAPLINK USB DEVICE AND REBOOT THE SYSTEM..', 0
00186     35 00000038 4D4F56452054484520-
00187     36 00000041 41524D204D42454420-
00188     37 0000004A 4441504C494E4B2055-
00189     38 00000053 534220444556494345-
00190     39 0000005C 20414E44205245424F-
00191     40 00000065 4F5420544845205359-
00192     41 0000006E 5354454D2E2E00
00193     42
00194     43 00000075 00<rept>                times BLLEN-($-$$) db 0
00195 
00196     USE BELOW SCRIPT TO COMPILE BOOTSTRAP AND GENERATE PAYLOAD:
00197     #!/usr/bin/env python
00198     import os
00199     os.system('nasm -f bin -o print.bin -l print.lst print.asm')
00200     print(open('print.lst','r').read())
00201     x=1
00202     for c in open('print.bin','rb').read():
00203             print('0x%02X, '%c, end='' if x % 16 else '\n')
00204             x += 1
00205      */
00206     /*uint8_t[448]*/.bootstrap = {
00207         0xFA, 0xB8, 0xC0, 0x07, 0x05, 0x20, 0x01, 0x8E, 0xD0, 0xBC, 0x00, 0x10, 0xB8, 0xC0, 0x07, 0x8E,
00208         0xD8, 0xBE, 0x6D, 0x00, 0xE8, 0x0B, 0x00, 0xEB, 0xFE, 0xB4, 0x0E, 0xB7, 0x00, 0xB3, 0x07, 0xCD,
00209         0x10, 0xC3, 0x8A, 0x04, 0x46, 0x08, 0xC0, 0x74, 0x05, 0xE8, 0xED, 0xFF, 0xEB, 0xF4, 0xC3, 0x50,
00210         0x4C, 0x45, 0x41, 0x53, 0x45, 0x20, 0x52, 0x45, 0x4D, 0x4F, 0x56, 0x45, 0x20, 0x54, 0x48, 0x45,
00211         0x20, 0x41, 0x52, 0x4D, 0x20, 0x4D, 0x42, 0x45, 0x44, 0x20, 0x44, 0x41, 0x50, 0x4C, 0x49, 0x4E,
00212         0x4B, 0x20, 0x55, 0x53, 0x42, 0x20, 0x44, 0x45, 0x56, 0x49, 0x43, 0x45, 0x20, 0x41, 0x4E, 0x44,
00213         0x20, 0x52, 0x45, 0x42, 0x4F, 0x4F, 0x54, 0x20, 0x54, 0x48, 0x45, 0x20, 0x53, 0x59, 0x53, 0x54,
00214         0x45, 0x4D, 0x2E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00215         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00216         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00217         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00218         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00219         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00220         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00221         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00222         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00223         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00224         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00225         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00226         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00227         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00228         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00229         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00230         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00231         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00232         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00233         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00234         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00235     },
00236     // Signature MUST be 0xAA55 to maintain compatibility (i.e. with Android).
00237     /*uint16_t*/.signature = 0xAA55,
00238 };
00239 
00240 enum virtual_media_idx_t {
00241     MEDIA_IDX_MBR = 0,
00242     MEDIA_IDX_FAT1,
00243     MEDIA_IDX_FAT2,
00244     MEDIA_IDX_ROOT_DIR,
00245 
00246     MEDIA_IDX_COUNT
00247 };
00248 
00249 // Note - everything in virtual media must be a multiple of VFS_SECTOR_SIZE
00250 const virtual_media_t virtual_media_tmpl[] = {
00251     /*  Read CB         Write CB        Region Size                 Region Name     */
00252     {   read_mbr,       write_none,     VFS_SECTOR_SIZE         },  /* MBR          */
00253     {   read_fat,       write_none,     0 /* Set at runtime */  },  /* FAT1         */
00254     {   read_fat,       write_none,     0 /* Set at runtime */  },  /* FAT2         */
00255     {   read_dir,       write_dir,      VFS_SECTOR_SIZE * 2     },  /* Root Dir     */
00256     /* Raw filesystem contents follow */
00257 };
00258 // Keep virtual_media_idx_t in sync with virtual_media_tmpl
00259 COMPILER_ASSERT(MEDIA_IDX_COUNT == ARRAY_SIZE(virtual_media_tmpl));
00260 
00261 static const FatDirectoryEntry_t root_dir_entry = {
00262     /*uint8_t[11] */ .filename = {""},
00263     /*uint8_t */ .attributes = VFS_FILE_ATTR_VOLUME_LABEL | VFS_FILE_ATTR_ARCHIVE,
00264     /*uint8_t */ .reserved = 0x00,
00265     /*uint8_t */ .creation_time_ms = 0x00,
00266     /*uint16_t*/ .creation_time = 0x0000,
00267     /*uint16_t*/ .creation_date = 0x0000,
00268     /*uint16_t*/ .accessed_date = 0x0000,
00269     /*uint16_t*/ .first_cluster_high_16 = 0x0000,
00270     /*uint16_t*/ .modification_time = 0x8E41,
00271     /*uint16_t*/ .modification_date = 0x32bb,
00272     /*uint16_t*/ .first_cluster_low_16 = 0x0000,
00273     /*uint32_t*/ .filesize = 0x00000000
00274 };
00275 
00276 static const FatDirectoryEntry_t dir_entry_tmpl = {
00277     /*uint8_t[11] */ .filename = {""},
00278     /*uint8_t */ .attributes = VFS_FILE_ATTR_READ_ONLY,
00279     /*uint8_t */ .reserved = 0x00,
00280     /*uint8_t */ .creation_time_ms = 0x00,
00281     /*uint16_t*/ .creation_time = 0x0000,
00282     /*uint16_t*/ .creation_date = 0x4876,
00283     /*uint16_t*/ .accessed_date = 0x4876,
00284     /*uint16_t*/ .first_cluster_high_16 = 0x0000,
00285     /*uint16_t*/ .modification_time = 0x83dc,
00286     /*uint16_t*/ .modification_date = 0x4876,
00287     /*uint16_t*/ .first_cluster_low_16 = 0x0000,
00288     /*uint32_t*/ .filesize = 0x00000000
00289 };
00290 
00291 mbr_t mbr;
00292 file_allocation_table_t fat;
00293 virtual_media_t virtual_media[16];
00294 root_dir_t dir_current;
00295 uint8_t file_count;
00296 vfs_file_change_cb_t file_change_cb;
00297 uint32_t virtual_media_idx;
00298 uint32_t fat_idx;
00299 uint32_t dir_idx;
00300 uint32_t data_start;
00301 
00302 // Virtual media must be larger than the template
00303 COMPILER_ASSERT(sizeof(virtual_media) > sizeof(virtual_media_tmpl));
00304 
00305 static void write_fat(file_allocation_table_t *fat, uint32_t idx, uint16_t val)
00306 {
00307     uint32_t low_idx;
00308     uint32_t high_idx;
00309     low_idx = idx * 2 + 0;
00310     high_idx = idx * 2 + 1;
00311 
00312     // Assert that this is still within the fat table
00313     if (high_idx >= ARRAY_SIZE(fat->f)) {
00314         util_assert(0);
00315         return;
00316     }
00317 
00318     fat->f[low_idx] = (val >> 0) & 0xFF;
00319     fat->f[high_idx] = (val >> 8) & 0xFF;
00320 }
00321 
00322 void vfs_init(const vfs_filename_t drive_name, uint32_t disk_size)
00323 {
00324     uint32_t i;
00325     uint32_t num_clusters;
00326     uint32_t total_sectors;
00327     // Clear everything
00328     memset(&mbr, 0, sizeof(mbr));
00329     memset(&fat, 0, sizeof(fat));
00330     fat_idx = 0;
00331     memset(&virtual_media, 0, sizeof(virtual_media));
00332     memset(&dir_current, 0, sizeof(dir_current));
00333     dir_idx = 0;
00334     file_count = 0;
00335     file_change_cb = file_change_cb_stub;
00336     virtual_media_idx = 0;
00337     data_start = 0;
00338     // Initialize MBR
00339     memcpy(&mbr, &mbr_tmpl, sizeof(mbr_t));
00340     total_sectors = ((disk_size + KB(64)) / mbr.bytes_per_sector);
00341     // Make sure this is the right size for a FAT16 volume
00342     if (total_sectors < FAT_CLUSTERS_MIN * mbr.sectors_per_cluster) {
00343         util_assert(0);
00344         total_sectors = FAT_CLUSTERS_MIN * mbr.sectors_per_cluster;
00345     } else if (total_sectors > FAT_CLUSTERS_MAX * mbr.sectors_per_cluster) {
00346         util_assert(0);
00347         total_sectors = FAT_CLUSTERS_MAX * mbr.sectors_per_cluster;
00348     }
00349     if (total_sectors >= 0x10000) {
00350         mbr.total_logical_sectors = 0;
00351         mbr.big_sectors_on_drive  = total_sectors;
00352     } else {
00353         mbr.total_logical_sectors = total_sectors;
00354         mbr.big_sectors_on_drive  = 0;
00355     }
00356     // FAT table will likely be larger than needed, but this is allowed by the
00357     // fat specification
00358     num_clusters = total_sectors / mbr.sectors_per_cluster;
00359     mbr.logical_sectors_per_fat = (num_clusters * 2 + VFS_SECTOR_SIZE - 1) / VFS_SECTOR_SIZE;
00360     // Initailize virtual media
00361     memcpy(&virtual_media, &virtual_media_tmpl, sizeof(virtual_media_tmpl));
00362     virtual_media[MEDIA_IDX_FAT1].length = VFS_SECTOR_SIZE * mbr.logical_sectors_per_fat;
00363     virtual_media[MEDIA_IDX_FAT2].length = VFS_SECTOR_SIZE * mbr.logical_sectors_per_fat;
00364     // Initialize indexes
00365     virtual_media_idx = MEDIA_IDX_COUNT;
00366     data_start = 0;
00367 
00368     for (i = 0; i < ARRAY_SIZE(virtual_media_tmpl); i++) {
00369         data_start += virtual_media[i].length;
00370     }
00371 
00372     // Initialize FAT
00373     fat_idx = 0;
00374     write_fat(&fat, fat_idx, 0xFFF8);    // Media type "media_descriptor"
00375     fat_idx++;
00376     write_fat(&fat, fat_idx, 0xFFFF);    // FAT12 - always 0xFFF (no meaning), FAT16 - dirty/clean (clean = 0xFFFF)
00377     fat_idx++;
00378     // Initialize root dir
00379     dir_idx = 0;
00380     dir_current.f[dir_idx] = root_dir_entry;
00381     memcpy(dir_current.f[dir_idx].filename, drive_name, sizeof(dir_current.f[0].filename));
00382     dir_idx++;
00383 }
00384 
00385 uint32_t vfs_get_total_size()
00386 {
00387     uint32_t size;
00388     if (mbr.total_logical_sectors > 0) {
00389         size = mbr.total_logical_sectors * mbr.bytes_per_sector;
00390     } else if (mbr.big_sectors_on_drive > 0) {
00391         size = mbr.big_sectors_on_drive * mbr.bytes_per_sector;
00392     } else {
00393         size = 0;
00394         util_assert(0);
00395     }
00396     return size;
00397 }
00398 
00399 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)
00400 {
00401     uint32_t first_cluster;
00402     FatDirectoryEntry_t *de;
00403     uint32_t clusters;
00404     uint32_t cluster_size;
00405     uint32_t i;
00406     util_assert(filename_valid(filename));
00407     // Compute the number of clusters in the file
00408     cluster_size = mbr.bytes_per_sector * mbr.sectors_per_cluster;
00409     clusters = (len + cluster_size - 1) / cluster_size;
00410     // Write the cluster chain to the fat table
00411     first_cluster = 0;
00412 
00413     if (len > 0) {
00414         first_cluster = fat_idx;
00415 
00416         for (i = 0; i < clusters - 1; i++) {
00417             write_fat(&fat, fat_idx, fat_idx + 1);
00418             fat_idx++;
00419         }
00420 
00421         write_fat(&fat, fat_idx, 0xFFFF);
00422         fat_idx++;
00423     }
00424 
00425     // Update directory entry
00426     if (dir_idx >= ARRAY_SIZE(dir_current.f)) {
00427         util_assert(0);
00428         return VFS_FILE_INVALID;
00429     }
00430 
00431     de = &dir_current.f[dir_idx];
00432     dir_idx++;
00433     memcpy(de, &dir_entry_tmpl, sizeof(dir_entry_tmpl));
00434     memcpy(de->filename, filename, 11);
00435     de->filesize = len;
00436     de->first_cluster_high_16 = (first_cluster >> 16) & 0xFFFF;
00437     de->first_cluster_low_16 = (first_cluster >> 0) & 0xFFFF;
00438 
00439     // Update virtual media
00440     if (virtual_media_idx >= ARRAY_SIZE(virtual_media)) {
00441         util_assert(0);
00442         return VFS_FILE_INVALID;
00443     }
00444 
00445     virtual_media[virtual_media_idx].read_cb = read_zero;
00446     virtual_media[virtual_media_idx].write_cb = write_none;
00447 
00448     if (0 != read_cb) {
00449         virtual_media[virtual_media_idx].read_cb = read_cb;
00450     }
00451 
00452     if (0 != write_cb) {
00453         virtual_media[virtual_media_idx].write_cb = write_cb;
00454     }
00455 
00456     virtual_media[virtual_media_idx].length = clusters * mbr.bytes_per_sector * mbr.sectors_per_cluster;
00457     virtual_media_idx++;
00458     file_count += 1;
00459     return de;
00460 }
00461 
00462 void vfs_file_set_attr(vfs_file_t file, vfs_file_attr_bit_t attr)
00463 {
00464     FatDirectoryEntry_t *de = file;
00465     de->attributes = attr;
00466 }
00467 
00468 vfs_sector_t vfs_file_get_start_sector(vfs_file_t file)
00469 {
00470     FatDirectoryEntry_t *de = file;
00471 
00472     if (vfs_file_get_size(file) == 0) {
00473         return VFS_INVALID_SECTOR;
00474     }
00475 
00476     return cluster_to_sector(de->first_cluster_low_16);
00477 }
00478 
00479 uint32_t vfs_file_get_size(vfs_file_t file)
00480 {
00481     FatDirectoryEntry_t *de = file;
00482     return de->filesize;
00483 }
00484 
00485 vfs_file_attr_bit_t vfs_file_get_attr(vfs_file_t file)
00486 {
00487     FatDirectoryEntry_t *de = file;
00488     return (vfs_file_attr_bit_t)de->attributes;
00489 }
00490 
00491 void vfs_set_file_change_callback(vfs_file_change_cb_t cb)
00492 {
00493     file_change_cb = cb;
00494 }
00495 
00496 void vfs_read(uint32_t requested_sector, uint8_t *buf, uint32_t num_sectors)
00497 {
00498     uint8_t i = 0;
00499     uint32_t current_sector;
00500     // Zero out the buffer
00501     memset(buf, 0, num_sectors * VFS_SECTOR_SIZE);
00502     current_sector = 0;
00503 
00504     for (i = 0; i < ARRAY_SIZE(virtual_media); i++) {
00505         uint32_t vm_sectors = virtual_media[i].length / VFS_SECTOR_SIZE;
00506         uint32_t vm_start = current_sector;
00507         uint32_t vm_end = current_sector + vm_sectors;
00508 
00509         // Data can be used in this sector
00510         if ((requested_sector >= vm_start) && (requested_sector < vm_end)) {
00511             uint32_t sector_offset;
00512             uint32_t sectors_to_write = vm_end - requested_sector;
00513             sectors_to_write = MIN(sectors_to_write, num_sectors);
00514             sector_offset = requested_sector - current_sector;
00515             virtual_media[i].read_cb(sector_offset, buf, sectors_to_write);
00516             // Update requested sector
00517             requested_sector += sectors_to_write;
00518             num_sectors -= sectors_to_write;
00519         }
00520 
00521         // If there is no more data to be read then break
00522         if (num_sectors == 0) {
00523             break;
00524         }
00525 
00526         // Move to the next virtual media entry
00527         current_sector += vm_sectors;
00528     }
00529 }
00530 
00531 void vfs_write(uint32_t requested_sector, const uint8_t *buf, uint32_t num_sectors)
00532 {
00533     uint8_t i = 0;
00534     uint32_t current_sector;
00535     current_sector = 0;
00536 
00537     for (i = 0; i < virtual_media_idx; i++) {
00538         uint32_t vm_sectors = virtual_media[i].length / VFS_SECTOR_SIZE;
00539         uint32_t vm_start = current_sector;
00540         uint32_t vm_end = current_sector + vm_sectors;
00541 
00542         // Data can be used in this sector
00543         if ((requested_sector >= vm_start) && (requested_sector < vm_end)) {
00544             uint32_t sector_offset;
00545             uint32_t sectors_to_read = vm_end - requested_sector;
00546             sectors_to_read = MIN(sectors_to_read, num_sectors);
00547             sector_offset = requested_sector - current_sector;
00548             virtual_media[i].write_cb(sector_offset, buf, sectors_to_read);
00549             // Update requested sector
00550             requested_sector += sectors_to_read;
00551             num_sectors -= sectors_to_read;
00552         }
00553 
00554         // If there is no more data to be read then break
00555         if (num_sectors == 0) {
00556             break;
00557         }
00558 
00559         // Move to the next virtual media entry
00560         current_sector += vm_sectors;
00561     }
00562 }
00563 
00564 static uint32_t read_zero(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
00565 {
00566     uint32_t read_size = VFS_SECTOR_SIZE * num_sectors;
00567     memset(data, 0, read_size);
00568     return read_size;
00569 }
00570 
00571 static void write_none(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
00572 {
00573     // Do nothing
00574 }
00575 
00576 static uint32_t read_mbr(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
00577 {
00578     uint32_t read_size = sizeof(mbr_t);
00579     COMPILER_ASSERT(sizeof(mbr_t) <= VFS_SECTOR_SIZE);
00580 
00581     if (sector_offset != 0) {
00582         // Don't worry about reading other sectors
00583         return 0;
00584     }
00585 
00586     memcpy(data, &mbr, read_size);
00587     return read_size;
00588 }
00589 
00590 /* No need to handle writes to the mbr */
00591 
00592 static uint32_t read_fat(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
00593 {
00594     uint32_t read_size = sizeof(file_allocation_table_t);
00595     COMPILER_ASSERT(sizeof(file_allocation_table_t) <= VFS_SECTOR_SIZE);
00596 
00597     if (sector_offset != 0) {
00598         // Don't worry about reading other sectors
00599         return 0;
00600     }
00601 
00602     memcpy(data, &fat, read_size);
00603     return read_size;
00604 }
00605 
00606 /* No need to handle writes to the fat */
00607 
00608 static uint32_t read_dir(uint32_t sector_offset, uint8_t *data, uint32_t num_sectors)
00609 {
00610     if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) {
00611         // Trying to read too much of the root directory
00612         util_assert(0);
00613         return 0;
00614     }
00615 
00616     // Zero buffer data is VFS_SECTOR_SIZE max
00617     memset(data, 0, VFS_SECTOR_SIZE);
00618 
00619     if (sector_offset == 0) { //Handle the first 512 bytes
00620         // Copy data that is actually created in the directory
00621         memcpy(data, &dir_current.f[0], dir_idx*sizeof(FatDirectoryEntry_t));
00622     }
00623 
00624     return num_sectors * VFS_SECTOR_SIZE;
00625 }
00626 
00627 static void write_dir(uint32_t sector_offset, const uint8_t *data, uint32_t num_sectors)
00628 {
00629     FatDirectoryEntry_t *old_entry;
00630     FatDirectoryEntry_t *new_entry;
00631     uint32_t start_index;
00632     uint32_t num_entries;
00633     uint32_t i;
00634 
00635     if ((sector_offset + num_sectors) * VFS_SECTOR_SIZE > sizeof(dir_current)) {
00636         // Trying to write too much of the root directory
00637         util_assert(0);
00638         return;
00639     }
00640 
00641     start_index = sector_offset * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
00642     num_entries = num_sectors * VFS_SECTOR_SIZE / sizeof(FatDirectoryEntry_t);
00643     old_entry = &dir_current.f[start_index];
00644     new_entry = (FatDirectoryEntry_t *)data;
00645     // If this is the first sector start at index 1 to get past drive name
00646     i = 0 == sector_offset ? 1 : 0;
00647 
00648     for (; i < num_entries; i++) {
00649         bool same_name;
00650 
00651         if (0 == memcmp(&old_entry[i], &new_entry[i], sizeof(FatDirectoryEntry_t))) {
00652             continue;
00653         }
00654 
00655         // If were at this point then something has changed in the file
00656         same_name = (0 == memcmp(old_entry[i].filename, new_entry[i].filename, sizeof(new_entry[i].filename))) ? 1 : 0;
00657         // Changed
00658         file_change_cb(new_entry[i].filename, VFS_FILE_CHANGED , (vfs_file_t)&old_entry[i], (vfs_file_t)&new_entry[i]);
00659 
00660         // Deleted
00661         if (0xe5 == (uint8_t)new_entry[i].filename[0]) {
00662             file_change_cb(old_entry[i].filename, VFS_FILE_DELETED , (vfs_file_t)&old_entry[i], (vfs_file_t)&new_entry[i]);
00663             continue;
00664         }
00665 
00666         // Created
00667         if (!same_name && filename_valid(new_entry[i].filename)) {
00668             file_change_cb(new_entry[i].filename, VFS_FILE_CREATED , (vfs_file_t)&old_entry[i], (vfs_file_t)&new_entry[i]);
00669             continue;
00670         }
00671     }
00672 
00673     memcpy(&dir_current.f[start_index], data, num_sectors * VFS_SECTOR_SIZE);
00674 }
00675 
00676 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)
00677 {
00678     // Do nothing
00679 }
00680 
00681 static uint32_t cluster_to_sector(uint32_t cluster_idx)
00682 {
00683     uint32_t sectors_before_data = data_start / mbr.bytes_per_sector;
00684     return sectors_before_data + (cluster_idx - 2) * mbr.sectors_per_cluster;
00685 }
00686 
00687 static bool filename_valid(const vfs_filename_t  filename)
00688 {
00689     // Information on valid 8.3 filenames can be found in
00690     // the microsoft hardware whitepaper:
00691     //
00692     // Microsoft Extensible Firmware Initiative
00693     // FAT32 File System Specification
00694     // FAT: General Overview of On-Disk Format
00695     const char invalid_starting_chars[] = {
00696         0xE5, // Deleted
00697         0x00, // Deleted (and all following entries are free)
00698         0x20, // Space not allowed as first character
00699     };
00700     uint32_t i;
00701 
00702     // Check for invalid starting characters
00703     for (i = 0; i < sizeof(invalid_starting_chars); i++) {
00704         if (invalid_starting_chars[i] == filename[0]) {
00705             return false;
00706         }
00707     }
00708 
00709     // Make sure all the characters are valid
00710     for (i = 0; i < sizeof(vfs_filename_t); i++) {
00711         if (!filename_character_valid(filename[i])) {
00712             return false;
00713         }
00714     }
00715 
00716     // All checks have passed so filename is valid
00717     return true;
00718 }
00719 
00720 static bool filename_character_valid(char character)
00721 {
00722     const char invalid_chars[] = {0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, 0x7C};
00723     uint32_t i;
00724 
00725     // Lower case characters are not allowed
00726     if ((character >= 'a') && (character <= 'z')) {
00727         return false;
00728     }
00729 
00730     // Values less than 0x20 are not allowed except 0x5
00731     if ((character < 0x20) && (character != 0x5)) {
00732         return false;
00733     }
00734 
00735     // Check for special characters that are not allowed
00736     for (i = 0; i < sizeof(invalid_chars); i++) {
00737         if (invalid_chars[i] == character) {
00738             return false;
00739         }
00740     }
00741 
00742     // All of the checks have passed so this is a valid file name character
00743     return true;
00744 }