Added file system documentation

This commit is contained in:
Sparchatus 2022-05-31 10:20:16 +00:00
parent 6d6c3b226f
commit 8df9742b25
2 changed files with 159 additions and 65 deletions

View File

@ -4,33 +4,23 @@
#include <fs/fs.h>
#include <drivers/sdhc.h>
#define FAT32_SHORTNAME_SIZE 8 // bytes
#define FAT32_SHORTNAME_EXT_SIZE 3 // bytes
#define FAT32_BLOCK_SIZE SDHC_BLOCK_SIZE
#define FAT32_SHORTNAME_SIZE 8 // number of bytes in a FAT32 shortname
#define FAT32_SHORTNAME_EXT_SIZE 3 // number of bytes in a FAT32 shortname extension
#define FAT32_BLOCK_SIZE SDHC_BLOCK_SIZE // static blocksize used by our implementation
#define FAT32_EOFC_MARK 0x0FFFFFF8
#define FAT32_EOFC_MARK 0x0FFFFFF8 // cluster entries equal to or larger than this value mark it as the end of the cluster chain
// "your FAT file system driver must not allow a cluster chain to be created that is longer than 0x100000000 bytes,
// and the last byte of the last cluster in a chain that long cannot be allocated to the file" - FAT32 spec
#define FAT32_MAX_FILESIZE (0x100000000 - 1)
#define FAT32_MAX_FILESIZE (0x100000000 - 1) // "your FAT file system driver must not allow a cluster chain to be created that is longer than 0x100000000 bytes, and the last byte of the last cluster in a chain that long cannot be allocated to the file" - FAT32 spec
/**
* ATTR_READ_ONLY Indicates that writes to the file should fail.
* ATTR_HIDDEN Indicates that normal directory listings should not show this file.
* ATTR_SYSTEM Indicates that this is an operating system file.
* ATTR_VOLUME_ID There should only be one file on the volume that has this attribute set, and that file must be in the root directory. This name of this file is actually the label for the volume. DIR_FstClusHI and DIR_FstClusLO must always be 0 for the volume label (no data clusters are allocated to the volume label file).
* ATTR_DIRECTORY Indicates that this file is actually a container for other files.
* // TODO rueegges: don't forget to set this
* ATTR_ARCHIVE This attribute supports backup utilities. This bit is set by the FAT file system driver when a file is created, renamed, or written to. Backup utilities may use this attribute to indicate which files on the volume have been modified since the last time that a backup was performed.
*/
#define FAT32_ATTR_READ_ONLY 0x01
#define FAT32_ATTR_HIDDEN 0x02
#define FAT32_ATTR_SYSTEM 0x04
#define FAT32_ATTR_VOLUME_ID 0x08
#define FAT32_ATTR_DIRECTORY 0x10
#define FAT32_ATTR_ARCHIVE 0x20
#define FAT32_ATTR_READ_ONLY 0x01 // Indicates that writes to the file should fail.
#define FAT32_ATTR_HIDDEN 0x02 // Indicates that normal directory listings should not show this file.
#define FAT32_ATTR_SYSTEM 0x04 // Indicates that this is an operating system file.
#define FAT32_ATTR_VOLUME_ID 0x08 // There should only be one “file” on the volume that has this attribute set, and that file must be in the root directory. This name of this file is actually the label for the volume. DIR_FstClusHI and DIR_FstClusLO must always be 0 for the volume label (no data clusters are allocated to the volume label file).
#define FAT32_ATTR_DIRECTORY 0x10 // Indicates that this file is actually a container for other files.
#define FAT32_ATTR_ARCHIVE 0x20 // This attribute supports backup utilities. This bit is set by the FAT file system driver when a file is created, renamed, or written to. Backup utilities may use this attribute to indicate which files on the volume have been modified since the last time that a backup was performed.
#define FAT32_ATTR_LONG_NAME (FAT32_ATTR_READ_ONLY|FAT32_ATTR_HIDDEN|FAT32_ATTR_SYSTEM|FAT32_ATTR_VOLUME_ID)
/* block driver functions used by the filesystem to operate on the disk (and synchronize) */
typedef errval_t (*fat_block_read_fn_t)(uint32_t sector_number, size_t offset, size_t size, void *dst);
typedef errval_t (*fat_block_write_fn_t)(uint32_t sector_number, size_t offset, size_t size, const void *src);
typedef errval_t (*fat_block_lock_fn_t)(void);
@ -39,11 +29,6 @@ typedef errval_t (*fat_block_register_handle_fn_t)(uint64_t directory_entry_id);
typedef errval_t (*fat_block_unregister_handle_fn_t)(uint64_t directory_entry_id);
typedef errval_t (*fat_block_count_handles_fn_t)(uint64_t directory_entry_id, size_t *count);
// typedef errval_t (*fat_block_lock_read_fn_t)(int block_index);
// typedef errval_t (*fat_block_lock_write_fn_t)(int block_index);
// typedef errval_t (*fat_block_unlock_read_fn_t)(int block_index);
// typedef errval_t (*fat_block_unlock_write_fn_t)(int block_index);
#pragma pack(1)
/**
* @brief FAT32 specific part of the boot sector
@ -123,43 +108,69 @@ union bpb_fat {
};
#pragma pack(1)
/**
* @brief Boot sector and BPB data at the start of the disk
*/
struct bpb {
uint8_t jmp_boot[3];
unsigned char oem_name[8];
/**
* @brief number of bytes in a sector
* We expect this to be 512
*/
uint16_t bytes_per_sector;
/**
* @brief number of consecutive sectors making up a cluster
*/
uint8_t sectors_per_cluster;
/**
* @brief number of sectors before the first FAT
*/
uint16_t reserved_sector_count;
// number of consecutive FAT copies (there is only one "main" FAT but n-1 copies)
// should be 2 in most cases but can be any value >= 1
/**
* @brief number of consecutive FAT copies (there is only one "main" FAT but n-1 copies)
* should be 2 in most cases but can be any value >= 1
*/
uint8_t fat_count;
// MUST be 0 for FAT32
/**
* @brief MUST be 0 for FAT32
*/
uint16_t root_entry_count;
// MUST be 0 for FAT32
/**
* @brief MUST be 0 for FAT32
*/
uint16_t total_sector_16_count;
/*
* valid values: 0xF0 0xF8 0xF9 0xFA 0xFB 0xFC 0xFD 0xFE 0xFF
/**
* @brief valid values: 0xF0 0xF8 0xF9 0xFA 0xFB 0xFC 0xFD 0xFE 0xFF
* not really used anymore
*/
uint8_t media;
// MUST be 0 for FAT32
/**
* @brief MUST be 0 for FAT32
*/
uint16_t fat_sectors_16_count;
// unsure. something to do with interrput 0x13
// don't think we need this
/**
* @brief unsure. something to do with interrput 0x13
* not used by us
*/
uint16_t sectors_per_track;
// unsure. something to do with interrput 0x13
// don't think we need this
/**
* @brief unsure. something to do with interrput 0x13
* not used by us
*/
uint16_t number_of_heads;
/**
* @brief number of sectors preceding the partition containing this FAT volume
*
* should be 0 for non-partitioned devices, operating system specific, mainly relevant for 0x13 interrupt
*
* not used by us
*/
uint32_t hidden_sector_count;
/**
* @brief total count of sectors on the volume
*/
uint32_t total_sector_32_count;
/**
* @brief FAT type specific information
*/
@ -168,14 +179,36 @@ struct bpb {
STATIC_ASSERT(sizeof(struct bpb) <= 512-2, "BPB struct may be at most 512-2 bytes long");
#pragma pack(1)
/**
* @brief structure to speed up free cluster searches and free space checks
*
* not used or updated by us
*/
struct fat32_fsinfo {
uint32_t lead_signature; // Value 0x41615252
/**
* @brief Value 0x41615252
*/
uint32_t lead_signature;
uint8_t _reserved1[480];
uint32_t structure_signature; // Value 0x61417272.
uint32_t free_count; // 0xFFFFFFFF means free count is unknown
uint32_t next_free; // 0xFFFFFFFF means not set, just a hint at which we should start looking for free clusters
/**
* @brief Value 0x61417272
*/
uint32_t structure_signature;
/**
* @brief number of free clusters
* 0xFFFFFFFF means free count is unknown
*/
uint32_t free_count;
/**
* @brief just a hint at which we should start looking for free clusters
* 0xFFFFFFFF means not set
*/
uint32_t next_free;
uint8_t _reserved2[12];
uint32_t trail_signature; // Value 0xAA550000
/**
* @brief Value 0xAA550000
*/
uint32_t trail_signature;
};
#pragma pack(1)
@ -186,6 +219,7 @@ struct fat32_fsinfo {
*/
struct fat32_directory_entry {
/**
* @brief name of a directory
* DIR_Name[0] == 0xE5, then the directory entry is free
* If DIR_Name[0] == 0x00, then the directory entry is free (same as for 0xE5), and there are no allocated directory entries after this on
* If DIR_Name[0] == 0x05, then the actual file name character for this byte is 0xE5. 0xE5 is actually a valid KANJI lead byte value for the character set used in Japan. The special 0x05 value is used so that this special file name case for Japan can be handled properly and not cause FAT file system code to think that the entry is free.
@ -198,39 +232,55 @@ struct fat32_directory_entry {
*/
char short_name[FAT32_SHORTNAME_SIZE];
char short_name_ext[FAT32_SHORTNAME_EXT_SIZE];
uint8_t attributes; // upper two bits are reserved and should be 0
/**
* @brief upper two bits are reserved and should be 0
* Flags are available as FAT32_ATTR_ defines
*/
uint8_t attributes;
uint8_t nt_reserved;
uint8_t creation_time_deci_seconds;
uint8_t _unknown[6];
uint8_t _unknown[7];
uint16_t first_cluster_number_high_word;
uint16_t write_time; // also creation is write
uint16_t write_date; // also creation is write
/**
* @brief Time of last write
*/
uint16_t write_time;
/**
* @brief Date of last write
*/
uint16_t write_date;
uint16_t first_cluster_number_low_word;
// zero for directories
uint32_t file_size; // in bytes
/**
* @brief file size in bytes
* zero for directories
*/
uint32_t file_size;
};
STATIC_ASSERT(sizeof(struct fat32_directory_entry) == 32, "FAT32 Directory entries are 32 bytes in size");
/**
* @brief Reference to a directory entry
*/
struct fat32_directory_entry_ref {
/**
* @brief sector number where the directory entry is contained
*/
uint32_t sector_number;
/**
* @brief index of the directory entry in the sector
*/
size_t index_in_sector;
};
struct fat32_path_resolve_result {
struct fat32_directory_entry directory_entry;
struct fat32_directory_entry_ref directory_entry_ref;
};
/**
* @brief Struct for an open fat32 file system
*
* Note: We don't format file systems so the boot sector never changes
* look at the bpb and bpb_fat32 structs for more info on the fields
* look at the bpb and bpb_fat32 structs for more info on the fields contained in here
*/
struct fat32 {
// block functions
// block driver functions
fat_block_read_fn_t read_object_fn;
fat_block_write_fn_t write_object_fn;
fat_block_lock_fn_t lock_fn;
@ -242,8 +292,7 @@ struct fat32 {
// very primitive implementation of mounting
char *mount;
// metadata
// metadata about the file system
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sector_count;
@ -256,29 +305,69 @@ struct fat32 {
// information calculated during init
// the first sector containing actual directory or file data
/**
* @brief the first sector containing actual directory or file data
*/
uint32_t first_data_sector;
// the largest valid cluster number
/**
* @brief the largest valid cluster number
*/
uint32_t max_cluster_number;
// the number of bytes in a cluster
/**
* @brief the number of bytes in a cluster
*/
size_t bytes_per_cluster;
};
/**
* @brief handle to a file or directory
*/
struct fat32_handle {
/**
* @brief indicates whether this is a directory or file handle
*/
bool is_dir;
/**
* @brief the first cluster of this directory or file for convenience purposes
*/
uint32_t first_cluster;
/**
* @brief cluster containing the current byte offset
*
* valid if != 0
*/
uint32_t current_cluster;
/**
* @brief current byte offset in the file or directory
*/
size_t byte_offset;
/**
* @brief reference to the directory entry for this file or directory
*/
struct fat32_directory_entry_ref directory_entry_ref;
};
#define FAT32_FAT_ENTRIES_PER_SECTOR (SDHC_BLOCK_SIZE / sizeof(uint32_t))
#define FAT32_FAT_ENTRIES_PER_SECTOR (SDHC_BLOCK_SIZE / sizeof(uint32_t)) // number of cluster chain entries that are in a sector
STATIC_ASSERT(FAT32_FAT_ENTRIES_PER_SECTOR * sizeof(uint32_t) == SDHC_BLOCK_SIZE, "fat does not align with blocks");
#define FAT32_DIRECTORY_ENTRIES_PER_SECTOR (SDHC_BLOCK_SIZE / sizeof(struct fat32_directory_entry))
#define FAT32_DIRECTORY_ENTRIES_PER_SECTOR (SDHC_BLOCK_SIZE / sizeof(struct fat32_directory_entry)) // number of directory entry structures that are in a sector
STATIC_ASSERT(FAT32_DIRECTORY_ENTRIES_PER_SECTOR * sizeof(struct fat32_directory_entry) == SDHC_BLOCK_SIZE, "directories do not align with blocks");
/**
* @brief Open a fat32 file system by initializing the fat32 structure
*
* @param return_fat32 fat32 structure to initialize
* @param read_block_fn block driver function to read parts of a block
* @param write_block_fn block driver function to write parts of a block
* @param lock_fn block driver function to lock the block driver for exclusive access
* @param unlock_fn block driver function to unlock the block driver
* @param register_handle_fn block driver function to register a handle to a directory entry
* @param unregister_handle_fn block driver function to unregister a registered handle to a directory entry
* @param count_handles_fn block driver function to get the number of handles to a directory entry
* @param mount literal path prefix that should be ignored
* @return errval_t
*/
errval_t fat32_init(
struct fat32 **return_fat32,
fat_block_read_fn_t read_block_fn,

View File

@ -20,6 +20,11 @@ static struct performance_context fat32_perf_context;
#endif
#define FAT32_RUN_LOCKED(s) { /*debug_printf("Locking in %s\n", __FUNCTION__);*/ FAT32_PERFORMANCE_START; err = fat32->lock_fn(); if (err_is_fail(err)) { DEBUG_ERR(err, "locking"); FAT32_PERFORMANCE_END; return err; } err = s; fat32->unlock_fn(); FAT32_PERFORMANCE_END; return err; }
struct fat32_path_resolve_result {
struct fat32_directory_entry directory_entry;
struct fat32_directory_entry_ref directory_entry_ref;
};
static void fat32_get_time_information(uint16_t *write_date, uint16_t *write_time) {
time_t t = time(NULL);
struct tm tm = *localtime(&t);