- Sequential file reading works

- FAT32 file system driver is defined
- Automount implemented
- Unified file operations implemented
- File listing implemented
This commit is contained in:
Wiesner András 2023-11-11 18:03:23 +01:00
parent af2db137f9
commit d7a5a49394
7 changed files with 446 additions and 67 deletions

View File

@ -1,6 +1,15 @@
#ifndef EMBPART_EMBPART_H
#define EMBPART_EMBPART_H
#include "fs/fat32/fat32.h"
#include "fs/fs_driver.h"
#include "mbr/mbr.h"
#include "mount/mount.h"
#include "file_interface.h"
#include "MassStorage.h"
#endif //EMBPART_EMBPART_H

View File

@ -15,6 +15,34 @@ static inline void fat32_copy_second_to_first_buffer() {
memcpy(buffer, buffer + SECTOR_SIZE, SECTOR_SIZE);
}
#define FETCH_MSTG const MassStorage * mstg = ctrl->mstg
#define READ_MSTG_TO_FIRST_BUFFER(idx) MSTG_READ(mstg, (idx), buffer)
#define READ_MSTG_TO_SECOND_BUFFER(idx) MSTG_READ(mstg, (idx), buffer + SECTOR_SIZE)
/**
* Check if entry is in the second half of the buffer and load the next sector if it was. Also maintain the sector counter and
* the *entry pointer
* @param ctrl pointer to FAT32 control block
* @param entry pointer to pointer (sic!) to a file entry residing in the buffer
* @param sector pointer to sector count corresponding to the first hald of the working buffer
*/
static inline void fat32_check_sector_crossing_and_load(const Fat32_CtrlBlock * ctrl, Fat32_FileEntry ** entry, uint32_t * sector) {
// if we crossed the first buffer - second buffer border, then
// copy the second buffer to the first one and load the next sector
// to the second buffer
if ((void *) *entry > (void *) (buffer + SECTOR_SIZE)) {
// copy the second buffer to the first one
fat32_copy_second_to_first_buffer();
(*sector)++;
FETCH_MSTG;
READ_MSTG_TO_SECOND_BUFFER(*sector + 1);
// modify the entry pointer, since it's been moved
*entry = (Fat32_FileEntry *) (((uint8_t *) (*entry)) - SECTOR_SIZE);
} // TODO also must pay attention on crossing not just sector, but cluster boundaries
}
/**
* Get first sector of a cluster. A cluster may be composed of multiple sectors.
* @param ctrl Pointer to FAT32 control block
@ -56,10 +84,6 @@ int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage *mstg) {
return 0;
}
#define FETCH_MSTG const MassStorage * mstg = ctrl->mstg
#define READ_MSTG_TO_FIRST_BUFFER(idx) MSTG_READ(mstg, (idx), buffer)
#define READ_MSTG_TO_SECOND_BUFFER(idx) MSTG_READ(mstg, (idx), buffer + SECTOR_SIZE)
/**
* Dummy function converting 16-bit Unicode character to UTF-8 type. Just chops the upper byte
* @param unicode Unicode character
@ -153,13 +177,14 @@ uint16_t fat32_get_file_entry(Fat32_FileEntry **const entry, char *entry_name) {
name_len = fat32_trim_str_right(entry_name); // trim residual whitespaces
} else { // if the entry has only a short name
memcpy(entry_name, finalEntry->short_fname, 8); // copy basename
entry_name[8] = '\0';
name_len = fat32_trim_str_right(entry_name); // trim whitespaces
if (!((attribs & FAT32_FATT_VOLUME) || (attribs & FAT32_FATT_DIRECTORY))) { // omit dot if volume label or directory
entry_name[name_len++] = '.'; // insert dot
}
memcpy(entry_name + name_len, finalEntry->extension, 3); // copy extension
name_len = fat32_trim_str_right(entry_name); // trim whitespaces
if (entry_name[name_len - 1] == '.') { // chop dot if no extension
if (!(attribs & FAT32_FATT_DIRECTORY) && (entry_name[name_len - 1] == '.')) { // chop dot if no extension and not directory
name_len--;
}
entry_name[name_len] = '\0'; // terminate string
@ -229,7 +254,8 @@ static inline uint32_t fat32_get_first_cluster(const Fat32_FileEntry *entry) {
}
/**
* The function locates a file based on its full path.
* The function locates a file based on its full path. If path is empty and root is requested, then it returns pointer to
* the first root directory ENTRY, not to a entry pointing to the root directory.
* @param ctrl pointer to a FAT32 control block
* @param path full path of the file to be located
* @param file_entry_ba if not NULL, then the function returns here the file entry's byte address (on the mass storage); untouched if file not found
@ -253,6 +279,13 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
char entry_name[FAT32_MAX_BASENAME_LENGTH + 1]; // entry name
Fat32_FileEntry *entry = NULL; // entry iterator
// handle opening the root, return pointer to first ENTRY in the root directory (see description!)
if (path[0] == '\0') {
file_found = true; // the root directory always exists
entry = (Fat32_FileEntry *) buffer; // root directory ENTRIES is in the root sector
}
// if not root directory, then...
while ((!file_found) && (!no_such_file)) { // traversing tree levels
// extract first basename
uint16_t basename_len = fat32_extract_next_basename_length(path_iter); // get basename length
@ -281,18 +314,21 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
entry++;
// if we crossed the first buffer - second buffer border, then
// copy the second buffer to the first one and load the next sector
// to the second buffer
if ((void *) entry > (void *) (buffer + SECTOR_SIZE)) {
// copy the second buffer to the first one
fat32_copy_second_to_first_buffer();
sector++;
READ_MSTG_TO_SECOND_BUFFER(sector + 1);
// check sector and cluster boundary crossing
fat32_check_sector_crossing_and_load(ctrl, &entry, &sector);
// modify the entry pointer, since it's been moved
entry = (Fat32_FileEntry *) (((uint8_t *) (entry)) - SECTOR_SIZE);
}
// // if we crossed the first buffer - second buffer border, then
// // copy the second buffer to the first one and load the next sector
// // to the second buffer
// if ((void *) entry > (void *) (buffer + SECTOR_SIZE)) {
// // copy the second buffer to the first one
// fat32_copy_second_to_first_buffer();
// sector++;
// READ_MSTG_TO_SECOND_BUFFER(sector + 1);
//
// // modify the entry pointer, since it's been moved
// entry = (Fat32_FileEntry *) (((uint8_t *) (entry)) - SECTOR_SIZE);
// }
// TODO also must pay attention on crossing not just sector, but cluster boundaries
}
@ -301,9 +337,6 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
if (basename_entry_found) {
if (path_iter[0] == '\0') { // ...and it's the end of the path
file_found = true; // ...then the file is found
if (file_entry_ba != NULL) { // if file entry writeback address if provided
*file_entry_ba = sector * ctrl->bytes_per_sector + (((uint8_t *) entry) - buffer);
}
} else { // ...if it's not the end of the path, then load the cluster corresponding to the entry
// determine sector number
uint32_t next_cluster = fat32_get_first_cluster(entry);
@ -322,48 +355,15 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
// return with appropriate value
if (!no_such_file) {
if (file_entry_ba != NULL) { // if file entry writeback address if provided
*file_entry_ba = sector * ctrl->bytes_per_sector + (((uint8_t *) entry) - buffer);
}
return entry;
} else {
return NULL;
}
}
// TODO subject to change, must include cluster crossing check
int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char *dir) {
// // TODO find directory
//
// // read root directory
// FETCH_MSTG;
// uint32_t sector = ctrl->root_s;
// READ_MSTG_TO_FIRST_BUFFER(sector);
// READ_MSTG_TO_SECOND_BUFFER(sector + 1);
//
// // acquire pointer to the beginning of the file entry table
// Fat32_FileEntry *entry = (Fat32_FileEntry *) buffer;
// while (entry->short_fname[0] != 0x00) {
// if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) {
// fat32_print_file_entry(&entry);
// }
// entry++;
//
// // if we crossed the first buffer - second buffer border, then
// // copy the second buffer to the first one and load the next sector
// // to the second buffer
// if ((void *) entry > (void *) (buffer + SECTOR_SIZE)) {
// // copy the second buffer to the first one
// fat32_copy_second_to_first_buffer();
// sector++;
// READ_MSTG_TO_SECOND_BUFFER(sector + 1);
//
// // modify the entry pointer, since it's been moved
// entry = (Fat32_FileEntry *) (((uint8_t *) (entry)) - SECTOR_SIZE);
// }
// }
//
// return 0;
return 0;
}
/**
* Get the sequence number of the sector holding the byte pointed by address.
* @param ctrl pointer to FAT32 control block
@ -614,6 +614,7 @@ uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *f
// cap length, don't read after the end of the file
uint32_t read_len = MIN(len, file_helper->size - seek_hint->pos);
uint32_t offset = 0;
uint32_t final_read_len = read_len; // a variable we will return with
// reading from a file may involve reading across cluster boundaries
@ -649,9 +650,100 @@ uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *f
seek_hint->pos += copy_size;
}
return read_len;
return final_read_len;
}
// ----------------------
Fs_Driver fat32_driver = { (fs_drv_open) fat32_open_file };
/**
* List directory. (I)
* @param ctrl pointer to FAT32 control block
* @param dir path of directory being listed
* @return 0 if successful, anything else refers to errors
*/
int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char *dir) {
uint32_t cluster, sector;
const Fat32_FileEntry *dir_entry = NULL;
if (dir[0] != '\0') { // if listing anything different from root directory
dir_entry = fat32_locate_file(ctrl, dir, NULL); // search dir entry
if (dir_entry == NULL) { // if file is not found at all
MSG("Directory '%s' could not be located!\n", dir);
return -1;
} else if ((dir[0] != '\0') && !(dir_entry->attributes & FAT32_FATT_DIRECTORY)) { // if found but it's no directory
MSG("It's not a directory!\n");
return 0;
}
cluster = fat32_get_first_cluster(dir_entry); // get first cluster
sector = fat32_get_first_sector_of_cluster(ctrl, cluster); // get first (absolute) sector of the cluster
} else { // if listing from the root sector
sector = ctrl->root_s; // get root directory's (absolute) sector
}
// fill buffers
FETCH_MSTG;
READ_MSTG_TO_FIRST_BUFFER(sector);
READ_MSTG_TO_SECOND_BUFFER(sector + 1);
// acquire pointer to the beginning of the file entry table
Fat32_FileEntry *entry = (Fat32_FileEntry *) buffer;
while (entry->short_fname[0] != 0x00) {
if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) {
fat32_print_file_entry(&entry);
}
entry++;
// check sector and cluster boundary crossing
fat32_check_sector_crossing_and_load(ctrl, &entry, &sector);
}
return 0;
}
/**
* Get FAT32 volume label.
* @param ctrl pointer to FAT32 control block
* @param label free space of at least 12 bytes (including terminating zero) for storing volume label
* @param maxLen maximum string length
*/
void fat32_get_volume_label(const Fat32_CtrlBlock * ctrl, char * label, uint16_t maxLen) {
// fill buffers
char entry_name[261];
uint32_t sector = ctrl->root_s;
FETCH_MSTG;
READ_MSTG_TO_FIRST_BUFFER(sector);
READ_MSTG_TO_SECOND_BUFFER(sector + 1);
// acquire pointer to the beginning of the file entry table
Fat32_FileEntry *entry = (Fat32_FileEntry *) buffer;
while (entry->short_fname[0] != 0x00) {
if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) {
fat32_get_file_entry(&entry, entry_name);
if (entry->attributes & FAT32_FATT_VOLUME) { // search for volume name entry
strncpy(label, entry_name, maxLen);
label[maxLen] = '\0';
}
}
entry++;
// check sector and cluster boundary crossing
fat32_check_sector_crossing_and_load(ctrl, &entry, &sector);
}
}
// ----------------------
static int fat32_drv_read(const void * ctrl, void * file, unsigned long len, void * buf) {
return (int) fat32_read_file_stream((Fat32_CtrlBlock *) ctrl, (Fat32_FileHelper *) file, len, (uint8_t *) buf);
}
static int fat32_drv_seek(const void * ctrl, void * file, unsigned long pos) {
return (int) fat32_seek_stream((Fat32_CtrlBlock *) ctrl, pos, (Fat32_FileHelper *) file);
}
Fs_Driver fat32_driver = {
(fs_drv_open) fat32_open_file, // open
(fs_drv_seek) fat32_drv_seek, // seek
(fs_drv_read) fat32_drv_read, // read
(fs_drv_list) fat32_list_dir // list files
};

View File

@ -11,6 +11,9 @@
#define MSG(...) printf(__VA_ARGS__)
#endif
#define FAT32_CHS_PART_ID (0x0B)
#define FAT32_LBA_PART_ID (0x0C)
typedef struct {
uint8_t boot_jump[3]; // jump instruction used for bootable volume
uint8_t formatter_name[8]; // identification of application or OS formatted this device
@ -123,5 +126,6 @@ uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_File
uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p);
void fat32_get_volume_label(const Fat32_CtrlBlock * ctrl, char * label, uint16_t maxLen);
#endif //EMBPART_FAT32_H

View File

@ -4,12 +4,14 @@
typedef int (*fs_drv_open)(const void * ctrl, const char * path, void * file);
typedef int (*fs_drv_seek)(const void * ctrl, void * file, unsigned long pos);
typedef int (*fs_drv_read)(const void * ctrl, void * file, unsigned long len, void * buf);
typedef int (*fs_drv_list)(const void * ctrl, const char * path);
// File system driver
typedef struct {
fs_drv_open open;
fs_drv_seek seek;
fs_drv_read read;
fs_drv_list list;
} Fs_Driver;
#endif //EMBPART_FS_DRIVER_H

View File

@ -3,6 +3,8 @@
#include <stdint.h>
#define MBR_MAX_PARTITIONS (4)
// partition entry
typedef struct {

View File

@ -1,5 +1,246 @@
#include <stddef.h>
#include <memory.h>
#include "mount.h"
#include "../MassStorage.h"
#include "../mbr/mbr.h"
#include "../fs/fat32/fat32.h"
int mnt_automount_disk() {
#define AUTOMOUNT_SECTOR_SIZE (512)
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
static Mnt_MountTable mtab = { 0 };
static Mnt_FileTable ftab = { 0 };
/**
* Attempts to mount a volume.
* @param mstg pointer to Mass Storage object
* @param part pointer to partition entry
* @return 0 if concluded without error (even if partition of unknown type could not be mounted)
*/
static int mnt_mount_volume(MassStorage * mstg, const PartEntry * part) {
// fetch common partition details
Mnt_Volume * next_entry = mtab.vols + mtab.mounted_vols; // pointer to control block area
uint8_t * cbk = next_entry->fs_control_block_area; // control block area to be written
next_entry->mnt_name[MNT_MAX_MOUNT_NAME_LENGTH] = '\0'; // write terminating zero in advance
int mount_success = -1;
// attempt to mount partition
switch (part->part_type) {
case 0: // empty slot
break;
case FAT32_CHS_PART_ID: // FAT32
case FAT32_LBA_PART_ID: {
Fat32_CtrlBlock *ctrl = (Fat32_CtrlBlock *) cbk;
mount_success = fat32_load(ctrl, part->lba_fa, mstg);
if (mount_success == 0) {
fat32_get_volume_label(ctrl, next_entry->mnt_name, MNT_MAX_MOUNT_NAME_LENGTH); // get volume name
next_entry->driver = fat32_driver; // assign driver
}
}
break;
default: // unmountable file system's found on the disk
MSG("Unknown file system type: %d, cannot mount!\n", part->part_type);
break;
}
// if succeeded, then maintain mount table
if (mount_success > -1) {
next_entry->part_size = part->sector_count * AUTOMOUNT_SECTOR_SIZE; // save volume size
mtab.mounted_vols++; // increase number of mounted partitions
}
return 0;
}
/**
* Attempt to automount disk volumes.
* @param mstg pointer to Mass Storage object
* @return 0 if successful
*/
int mnt_automount_disk(MassStorage * mstg) {
// load the first sector
uint8_t buf[AUTOMOUNT_SECTOR_SIZE];
mstg->read_sector(mstg, 0, buf);
// fetch partitions from the disk
const PartEntry * partList = mbr_get_partitions(buf);
if (partList == NULL) { // not an MBR disk
return 0;
}
// if it's an MBR disk, iterate over all four partition slots
mtab.mounted_vols = 0;
PartEntry parts[MBR_MAX_PARTITIONS];
memcpy(parts, partList, sizeof(PartEntry) * MBR_MAX_PARTITIONS); // copy partition table
for (uint8_t i = 0; (i < MBR_MAX_PARTITIONS) && (mtab.mounted_vols < MNT_MAX_MOUNTED_VOLUMES); i++) {
const PartEntry * part = parts + i; // fetch a partition
mnt_mount_volume(mstg, part); // attempt mounting a volume
}
return 0;
}
/**
* Get volume by name.
* @param name volume name
* @return pointer to volume, if found OR NULL
*/
Mnt_Volume * mnt_get_volume_by_name(const char * name) {
Mnt_Volume * vol = NULL;
for (uint8_t i = 0; i < mtab.mounted_vols; i++) {
if (!strncmp(mtab.vols[i].mnt_name, name, MNT_MAX_MOUNT_NAME_LENGTH)) {
vol = mtab.vols + i;
break;
}
}
return vol;
}
#define MNT_DIRSEP '/'
/**
* Separate volume name and the path relative to the volume's root.
* @param volpath string of /volume/path
* @param volname pointer to free area up to max_volname_len bytes
* @param max_volname_len size of free area not including terminating zero
* @return pointer to the relative path
*/
const char * mnt_separate_volume_path(const char * volpath, char * volname, uint16_t max_volname_len) {
const char * iter = volpath;
// strip first '/'
if (*iter == MNT_DIRSEP) {
iter++;
}
// search the end of volume name
uint16_t i = 0;
while ((*iter != MNT_DIRSEP) && (*iter != '\0')) {
if (i < max_volname_len) { // copy only so many that we can store
volname[i++] = *iter;
}
iter++;
}
volname[i] = '\0';
// skip '/' at the end of the volume name
if (*iter == MNT_DIRSEP) {
iter++;
}
return iter;
}
/**
* Get volume by name extracted from a file's path stored on the volume.
* @param volpath path beginning with the /volume name/
* @param path path of the file on the volume (relative to volume root)
* @return pointer to a mounted volume OR NULL if not found
*/
Mnt_Volume * mnt_get_volume_by_path(const char * volpath, const char ** path) {
const char * ret;
char volname[MNT_MAX_MOUNT_NAME_LENGTH + 1];
ret = mnt_separate_volume_path(volpath, volname, MNT_MAX_MOUNT_NAME_LENGTH);
if (path != NULL) {
*path = ret;
}
return mnt_get_volume_by_name(volname);
}
#define MNT_FTAB_BLOCK_FREE (0)
#define MNT_FTAB_BLOCK_OCCUPIED (1)
/**
* Allocate space for a newly opened file's helper object.
* @return allocated slot OR NULL is allocation was not possible
*/
Mnt_File * mnt_alloc_file_helper() {
Mnt_File * slot = NULL;
for (uint8_t i = 0; i < MNT_MAX_OPEN_FILES; i++) {
if (ftab.files[i].ctrl_word == MNT_FTAB_BLOCK_FREE) {
ftab.files[i].ctrl_word = MNT_FTAB_BLOCK_OCCUPIED;
slot = ftab.files + i;
break;
}
}
return slot;
}
/**
* Release file helper.
* @param helper pointer to helper
*/
void mnt_free_file_helper(Mnt_File * file) {
file->ctrl_word = MNT_FTAB_BLOCK_FREE;
}
// -----------------------
/**
* List directory.
* @param dir path to the directory
*/
void mnt_list(const char *dir) {
// if
if ((dir[0] == '\0') || ((dir[0] == MNT_DIRSEP) && (dir[1] == '\0'))) {
for (uint8_t i = 0; i < mtab.mounted_vols; i++) {
MSG("/%s VOLUME\n", mtab.vols[i].mnt_name);
}
return;
}
// if not empty path is specified
const char * relpath;
Mnt_Volume * vol = mnt_get_volume_by_path(dir, &relpath);
if (vol != NULL) {
(vol->driver).list((void *) vol->fs_control_block_area, relpath);
}
}
/**
* Open file.
* @param path full path to file
* @return pointer to file handle
*/
Mnt_File * mnt_open(const char * path) {
Mnt_File * file = NULL;
const char * relpath;
Mnt_Volume * vol = mnt_get_volume_by_path(path, &relpath);
if (vol != NULL) {
file = mnt_alloc_file_helper();
if (file != NULL) {
file->vol = vol;
(vol->driver).open(vol->fs_control_block_area, relpath, file->helper);
}
}
return file;
}
/**
* Read from the file.
* @param file pointer to Mnt_File object
* @param len (desired) read length
* @param buf destination buffer
* @return actual read length
*/
int mnt_read(Mnt_File * file, unsigned long len, void * buf) {
Mnt_Volume * vol = file->vol;
return vol->driver.read(vol->fs_control_block_area, (void *) file->helper, len, buf);
}
/**
* Seek the file.
* @param file pointer to Mnt_File object
* @param pos position from the beginning
* @return ???
*/
int mnt_seek(Mnt_File * file, unsigned long pos) {
Mnt_Volume * vol = file->vol;
return vol->driver.seek(vol->fs_control_block_area, (void *) file->helper, pos);
}
void mnt_close(Mnt_File *file) {
mnt_free_file_helper(file);
}

View File

@ -2,18 +2,47 @@
#define EMBPART_MOUNT_H
#include <stdint.h>
#include "../MassStorage.h"
#include "../fs/fs_driver.h"
#define MNT_MAX_MOUNTED_VOLUMES (4)
#define MNT_MAX_MOUNT_NAME_LENGTH (15)
#define MNT_CTRL_BLOCK_PREALLOC_SIZE (64)
#define MNT_MAX_OPEN_FILES (6)
#define MNT_FILE_HELPER_PREALLOC_SIZE (47)
typedef struct {
void * fs_control_block; // pointer to file system control block
uint32_t part_size; // partition size
char mnt_name[MNT_MAX_MOUNT_NAME_LENGTH + 1]; // name of the mount
} Mnt_MountEntry;
Fs_Driver driver; // file system driver
uint8_t fs_control_block_area[MNT_CTRL_BLOCK_PREALLOC_SIZE]; // pointer to file system control block
} Mnt_Volume;
typedef struct {
Mnt_MountEntry vols[MNT_MAX_MOUNTED_VOLUMES]; // volume slots
Mnt_Volume vols[MNT_MAX_MOUNTED_VOLUMES]; // volume slots
uint8_t mounted_vols; // number of mounted volumes
} Mnt_MountTable;
typedef struct {
Mnt_Volume * vol; // pointer to volume
uint8_t helper[MNT_FILE_HELPER_PREALLOC_SIZE]; // pointer to helper object
uint8_t ctrl_word; // control word
} Mnt_File;
typedef struct {
Mnt_File files[MNT_MAX_OPEN_FILES]; // file helpers
} Mnt_FileTable;
int mnt_automount_disk(MassStorage * mstg); // attempt to automount volumes
void mnt_list(const char * dir);
Mnt_File * mnt_open(const char * path);
int mnt_read(Mnt_File * file, unsigned long len, void * buf);
int mnt_seek(Mnt_File * file, unsigned long pos);
void mnt_close(Mnt_File * file);
#endif //EMBPART_MOUNT_H