312 lines
12 KiB
C
312 lines
12 KiB
C
#ifndef _INIT_FAT32_H_
|
||
#define _INIT_FAT32_H_
|
||
|
||
#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_EOFC_MARK 0x0FFFFFF8
|
||
|
||
// "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)
|
||
|
||
/**
|
||
* 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_LONG_NAME (FAT32_ATTR_READ_ONLY|FAT32_ATTR_HIDDEN|FAT32_ATTR_SYSTEM|FAT32_ATTR_VOLUME_ID)
|
||
|
||
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);
|
||
typedef errval_t (*fat_block_unlock_fn_t)(void);
|
||
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
|
||
*/
|
||
struct bpb_fat32 {
|
||
/**
|
||
* @brief count of sectors occupied by a single FAT
|
||
*/
|
||
uint32_t fat_sectors_32_count;
|
||
/**
|
||
* @brief only valid if mirror flag is set. zero based index of active FAT
|
||
*/
|
||
uint16_t ext_flag_active_fat:4;
|
||
uint16_t _ext_flag_reserved_1:3;
|
||
/**
|
||
* @brief 0 -> FAT is mirrored to all FATs, 1-> single FAT is active (specified in bits 0-3)
|
||
*/
|
||
uint16_t ext_flag_mirrored:1;
|
||
uint16_t _ext_flag_reserved_2:8;
|
||
/**
|
||
* @brief Currently only version 0 specified
|
||
*/
|
||
uint16_t fs_version;
|
||
/**
|
||
* @brief cluster number of the first cluster of the root directory
|
||
*
|
||
* usually 2
|
||
*/
|
||
uint32_t root_cluster_number;
|
||
/**
|
||
* @brief sector number of the fs infor structure in the reserved area of the FAT32 volume
|
||
*
|
||
* usually 1
|
||
*/
|
||
uint16_t fs_info_sector_number;
|
||
/**
|
||
* @brief if non-zero indicates the sector in the reserved area of the volume containing a copy of the bpb
|
||
*
|
||
* usually 6
|
||
*/
|
||
uint16_t backup_boot_sector_number;
|
||
uint8_t _reserved[12];
|
||
/**
|
||
* @brief int 0x13 drive number
|
||
*
|
||
* don't think we need this
|
||
*/
|
||
uint8_t drive_number;
|
||
uint8_t _reserved1;
|
||
/**
|
||
* @brief indicates that the next 3 fields are present
|
||
* 0x29 means that the next 3 fields are present? no other values specified...
|
||
*/
|
||
uint8_t extended_boot_signature;
|
||
/**
|
||
* @brief together with volume_label allows volume tracking on removable media
|
||
*
|
||
*/
|
||
uint32_t volume_serial_number;
|
||
/**
|
||
* @brief same as the 11-byte volume label recorded in the root directory
|
||
* "NO NAME " means that there is no label defined
|
||
*/
|
||
char volume_label[11];
|
||
/**
|
||
* @brief Always set to "FAT32 " for FAT32 file systems
|
||
*
|
||
* MUST NOT be used for file system type determination.
|
||
* just seems to be here for fun?
|
||
*/
|
||
char file_system_type[8];
|
||
};
|
||
|
||
#pragma pack(1)
|
||
union bpb_fat {
|
||
struct bpb_fat32 bpb_fat32;
|
||
};
|
||
|
||
#pragma pack(1)
|
||
struct bpb {
|
||
uint8_t jmp_boot[3];
|
||
unsigned char oem_name[8];
|
||
uint16_t bytes_per_sector;
|
||
uint8_t sectors_per_cluster;
|
||
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
|
||
uint8_t fat_count;
|
||
// MUST be 0 for FAT32
|
||
uint16_t root_entry_count;
|
||
// MUST be 0 for FAT32
|
||
uint16_t total_sector_16_count;
|
||
/*
|
||
* valid values: 0xF0 0xF8 0xF9 0xFA 0xFB 0xFC 0xFD 0xFE 0xFF
|
||
* not really used anymore
|
||
*/
|
||
uint8_t media;
|
||
// MUST be 0 for FAT32
|
||
uint16_t fat_sectors_16_count;
|
||
// unsure. something to do with interrput 0x13
|
||
// don't think we need this
|
||
uint16_t sectors_per_track;
|
||
// unsure. something to do with interrput 0x13
|
||
// don't think we need this
|
||
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
|
||
*/
|
||
uint32_t hidden_sector_count;
|
||
/**
|
||
* @brief total count of sectors on the volume
|
||
*/
|
||
uint32_t total_sector_32_count;
|
||
|
||
/**
|
||
* @brief FAT type specific information
|
||
*/
|
||
union bpb_fat bpb_fat;
|
||
};
|
||
STATIC_ASSERT(sizeof(struct bpb) <= 512-2, "BPB struct may be at most 512-2 bytes long");
|
||
|
||
#pragma pack(1)
|
||
struct fat32_fsinfo {
|
||
uint32_t lead_signature; // Value 0x41615252
|
||
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
|
||
uint8_t _reserved2[12];
|
||
uint32_t trail_signature; // Value 0xAA550000
|
||
};
|
||
|
||
#pragma pack(1)
|
||
/**
|
||
* @brief Single entry of a directory
|
||
* a directory is like a file but containing a list of structs of this type
|
||
*
|
||
*/
|
||
struct fat32_directory_entry {
|
||
/**
|
||
* 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.
|
||
*
|
||
* The DIR_Name field is actually broken into two parts+ the 8-character main part of the name, and the 3-character extension. These two parts are “trailing space padded” with bytes of 0x20.
|
||
* DIR_Name[0] may not equal 0x20. There is an implied ‘.’ character between the main part of the name and the extension part of the name that is not present in DIR_Name
|
||
* Lower case characters are not allowed in DIR_Name (what these characters are is country specific). The following characters are not legal in any bytes of DIR_Name:
|
||
* - Values less than 0x20 except for the special case of 0x05 in DIR_Name[0] described above.
|
||
* - 0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D, and 0x7C
|
||
*/
|
||
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
|
||
uint8_t nt_reserved;
|
||
uint8_t creation_time_deci_seconds;
|
||
uint8_t _unknown[6];
|
||
|
||
uint16_t first_cluster_number_high_word;
|
||
uint16_t write_time; // also creation is write
|
||
uint16_t write_date; // also creation is write
|
||
uint16_t first_cluster_number_low_word;
|
||
// zero for directories
|
||
uint32_t file_size; // in bytes
|
||
};
|
||
STATIC_ASSERT(sizeof(struct fat32_directory_entry) == 32, "FAT32 Directory entries are 32 bytes in size");
|
||
|
||
struct fat32_directory_entry_ref {
|
||
uint32_t sector_number;
|
||
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
|
||
*/
|
||
struct fat32 {
|
||
|
||
// block functions
|
||
fat_block_read_fn_t read_object_fn;
|
||
fat_block_write_fn_t write_object_fn;
|
||
fat_block_lock_fn_t lock_fn;
|
||
fat_block_lock_fn_t unlock_fn;
|
||
fat_block_register_handle_fn_t register_handle_fn;
|
||
fat_block_unregister_handle_fn_t unregister_handle_fn;
|
||
fat_block_count_handles_fn_t count_handles_fn;
|
||
|
||
// very primitive implementation of mounting
|
||
char *mount;
|
||
|
||
// metadata
|
||
|
||
uint16_t bytes_per_sector;
|
||
uint8_t sectors_per_cluster;
|
||
uint16_t reserved_sector_count;
|
||
uint8_t fat_count;
|
||
uint32_t total_sector_32_count;
|
||
uint32_t fat_sectors_32_count;
|
||
uint8_t ext_flag_active_fat:4;
|
||
uint8_t ext_flag_mirrored:1;
|
||
uint32_t root_cluster_number;
|
||
|
||
// information calculated during init
|
||
|
||
// the first sector containing actual directory or file data
|
||
uint32_t first_data_sector;
|
||
// the largest valid cluster number
|
||
uint32_t max_cluster_number;
|
||
// the number of bytes in a cluster
|
||
size_t bytes_per_cluster;
|
||
};
|
||
|
||
struct fat32_handle {
|
||
bool is_dir;
|
||
|
||
uint32_t first_cluster;
|
||
uint32_t current_cluster;
|
||
size_t byte_offset;
|
||
|
||
struct fat32_directory_entry_ref directory_entry_ref;
|
||
};
|
||
|
||
#define FAT32_FAT_ENTRIES_PER_SECTOR (SDHC_BLOCK_SIZE / sizeof(uint32_t))
|
||
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))
|
||
STATIC_ASSERT(FAT32_DIRECTORY_ENTRIES_PER_SECTOR * sizeof(struct fat32_directory_entry) == SDHC_BLOCK_SIZE, "directories do not align with blocks");
|
||
|
||
errval_t fat32_init(
|
||
struct fat32 **return_fat32,
|
||
fat_block_read_fn_t read_block_fn,
|
||
fat_block_write_fn_t write_block_fn,
|
||
fat_block_lock_fn_t lock_fn,
|
||
fat_block_unlock_fn_t unlock_fn,
|
||
fat_block_register_handle_fn_t register_handle_fn,
|
||
fat_block_unregister_handle_fn_t unregister_handle_fn,
|
||
fat_block_count_handles_fn_t count_handles_fn,
|
||
char *mount
|
||
);
|
||
|
||
// dirs
|
||
errval_t fat32_mkdir(struct fat32 *fat32, const char *path);
|
||
errval_t fat32_rmdir(struct fat32 *fat32, const char *path);
|
||
errval_t fat32_opendir(struct fat32 *fat32, const char *path, struct fat32_handle **dir_handle);
|
||
errval_t fat32_closedir(struct fat32 *fat32, struct fat32_handle *dir_handle);
|
||
errval_t fat32_stat(struct fat32 *fat32, struct fat32_handle *dir_handle, struct fs_fileinfo *fileinfo);
|
||
errval_t fat32_readdir(struct fat32 *fat32, struct fat32_handle *dir_handle, char **name);
|
||
errval_t fat32_rm(struct fat32 *fat32, const char *path);
|
||
|
||
// files
|
||
errval_t fat32_fopen(struct fat32 *fat32, const char *path, struct fat32_handle **file_handle);
|
||
errval_t fat32_fcreate(struct fat32 *fat32, const char *path, struct fat32_handle **file_handle);
|
||
errval_t fat32_fread(struct fat32 *fat32, struct fat32_handle *file_handle, void *buffer, size_t bytes, size_t *bytes_read);
|
||
errval_t fat32_fwrite(struct fat32 *fat32, struct fat32_handle *file_handle, const void *buffer, size_t bytes, size_t *bytes_written);
|
||
errval_t fat32_fclose(struct fat32 *fat32, struct fat32_handle *file_handle);
|
||
errval_t fat32_seek(struct fat32 *fat32, struct fat32_handle *file_handle, enum fs_seekpos whence, off_t offset);
|
||
errval_t fat32_tell(struct fat32 *fat32, struct fat32_handle *file_handle, size_t *pos);
|
||
|
||
#endif /* _INIT_FAT32_H_ */ |