From 8b8dcbe17024a24a2e99b63226debc67586abfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiesner=20Andr=C3=A1s?= Date: Tue, 3 Oct 2023 15:37:39 +0200 Subject: [PATCH] stream-like access added --- fs/fat32/fat32.c | 179 +++++++++++++++++++++++++++++++++++++++++++++-- fs/fat32/fat32.h | 16 +++-- 2 files changed, 184 insertions(+), 11 deletions(-) diff --git a/fs/fat32/fat32.c b/fs/fat32/fat32.c index 08347c5..c6e315a 100644 --- a/fs/fat32/fat32.c +++ b/fs/fat32/fat32.c @@ -236,11 +236,12 @@ static inline uint32_t fat32_get_first_cluster(const Fat32_FileEntry *entry) { * The function locates a file based on its full path. * @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 * @return pointer to the file entry. Caution! This pointer is only valid until no further * call is made to fat32_* functions, since all functions involve the same buffer. * Functions invalidating buffer content are marked with (I). */ -const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path) { +const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path, uint32_t *file_entry_ba) { FETCH_MSTG; // start search in the root directory @@ -256,7 +257,7 @@ 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 - while ((!file_found) && (!no_such_file)) { + 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 uint16_t copy_len = MIN(basename_len, FAT32_MAX_BASENAME_LENGTH); @@ -271,7 +272,7 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char // acquire pointer to the beginning of the file entry table entry = (Fat32_FileEntry *) buffer; bool basename_entry_found = false; - while ((entry->short_fname[0] != 0x00) && (!basename_entry_found)) { + while ((entry->short_fname[0] != 0x00) && (!basename_entry_found)) { // traversing siblings if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) { fat32_get_file_entry(&entry, entry_name); @@ -304,6 +305,9 @@ 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); @@ -312,6 +316,8 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char // load sectors READ_MSTG_TO_FIRST_BUFFER(next_sector); READ_MSTG_TO_SECOND_BUFFER(next_sector + 1); + + sector = next_sector; // advance sector } } else { // if not found... no_such_file = true; // ...then the file could not be located @@ -362,6 +368,30 @@ int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char *dir) { return 0; } +/** + * Get the sequence number of the sector holding the byte pointed by address. + * @param ctrl pointer to FAT32 control block + * @param address byte address + * @return sequence number of the requested sector + */ +static inline uint32_t fat32_get_sector_by_byte_address(const Fat32_CtrlBlock *ctrl, uint32_t ba) { + return ba / ctrl->bytes_per_sector; +} + +/** + * Get file entry based on its byte address. (I) + * @param ctrl pointer to FAT32 control block + * @param ba byte address of the file entry + * @return pointer to file entry + */ +static inline const Fat32_FileEntry * fat32_get_file_entry_by_byte_address(const Fat32_CtrlBlock * ctrl, uint32_t ba) { + uint32_t sector = fat32_get_sector_by_byte_address(ctrl, ba); + uint32_t offset = ba % ctrl->bytes_per_sector; + FETCH_MSTG; + READ_MSTG_TO_FIRST_BUFFER(sector); + return (Fat32_FileEntry *)(buffer + offset); +} + /** * Get the sector's sequence number corresponding to the specific cluster identified by its sequence number. * @param ctrl pointer to FAT32 control block @@ -373,7 +403,7 @@ static inline uint32_t fat32_get_fat_entry_sector_by_cluster_index(const Fat32_C } /** - * Gets the sequence number of the next chained cluster. (I) + * Get the sequence number of the next chained cluster. (I) * @param ctrl pointer to FAT32 control block * @param cluster sequence number of a cluster * @return sequence number of the cluster following the passed one in the chain @@ -388,7 +418,7 @@ static inline uint32_t fat32_get_next_chained_cluster(const Fat32_CtrlBlock *ctr } /** - * Get sequence number of the cluster storing a specific byte position (pos) if the file's first cluster is given. + * Get sequence number of the cluster storing a specific byte position (pos) if the file's first cluster is given. (I) * @param ctrl pointer to FAT32 control block * @param first_cluster sequence number of the first cluster of the file * @param pos byte position @@ -426,7 +456,8 @@ static inline uint32_t fat32_get_bytes_per_cluster(const Fat32_CtrlBlock *ctrl) } /** - * Read a file using "random access". + * Read a file using "random access". (I) + * This function holds a load of redundant code, but this is with intent. * @param ctrl pointer to FAT32 control block * @param entry pointer to the corresponding file entry * @param pos read start position @@ -434,7 +465,7 @@ static inline uint32_t fat32_get_bytes_per_cluster(const Fat32_CtrlBlock *ctrl) * @param p pointer to output buffer * @return the number of bytes read */ -uint32_t fat32_read_file(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *entry, uint32_t pos, uint32_t len, uint8_t *p) { +uint32_t fat32_read_file_random_access(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *entry, uint32_t pos, uint32_t len, uint8_t *p) { // check position validity and check that the entry corresponds to a file, not to a directory or volume uint8_t attr = entry->attributes; if ((pos >= entry->size) || (attr & FAT32_FATT_DIRECTORY) || (attr & FAT32_FATT_VOLUME)) { @@ -488,5 +519,139 @@ uint32_t fat32_read_file(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *ent offset += copy_size; } + return read_len; +} + +// ----------------- + +int fat32_open_file(const Fat32_CtrlBlock *ctrl, const char *path, Fat32_FileHelper *file_helper) { + uint32_t file_entry_ba; + const Fat32_FileEntry *entry = fat32_locate_file(ctrl, path, &file_entry_ba); + uint32_t first_cluster = fat32_get_first_cluster(entry); + + // store data usable for identifying file on the storage + file_helper->ctrl = ctrl; + file_helper->entry_ba = file_entry_ba; + + // store file information + file_helper->size = entry->size; + + // seek to zero + file_helper->last_pos.pos = 0; + file_helper->last_pos.sector_begin_pos = 0; + file_helper->last_pos.cluster = first_cluster; + file_helper->last_pos.sector = fat32_get_first_sector_of_cluster(ctrl, first_cluster); + file_helper->last_pos.sector_of_cluster = 0; + + return 0; +} + +/** + * Seek file stream, set reader to pos. + * @param ctrl pointer to FAT32 control block + * @param pos stream position + * @param file_helper pointer to file helper, which gets updated + * @return 0 on success + */ +uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_FileHelper *file_helper) { + // check that we are not attempting to seek out of the file + if (pos >= file_helper->size) { + return -1; + } + + // actual seeking + + Fat32_SeekHint *seek_hint = &file_helper->last_pos; // fetch seek hint + if (pos > file_helper->last_pos.pos) { // if seeking forward, then seeking can be accelerated using the seek hints + uint32_t delta = pos - file_helper->last_pos.pos; + uint32_t bytes_to_sector_end = ctrl->bytes_per_sector - (file_helper->last_pos.pos % ctrl->bytes_per_sector); // determine byte count to sector end + if (delta < bytes_to_sector_end) { // if seeking does not cross sector boundary + seek_hint->pos = pos; // just change position + } else { // ...if crosses sector boundary + uint32_t sector_boundaries_crossed = 1 + (delta - bytes_to_sector_end) / ctrl->bytes_per_sector; // 1: boundary crossed; further boundaries crossed based on delta + uint32_t delta_sector_from_clusters_first_sector = seek_hint->sector_of_cluster + sector_boundaries_crossed; + uint32_t cluster_boundaries_crossed = delta_sector_from_clusters_first_sector / ctrl->sectors_per_cluster; // number of cluster boundary crossing + if (cluster_boundaries_crossed > 0) { // if we crossed a cluster boundary, then load the next cluster's first sector in chain + uint32_t cluster = seek_hint->cluster; + for (uint32_t i = 0; i < cluster_boundaries_crossed; i++) { // do the cluster crossings + cluster = fat32_get_next_chained_cluster(ctrl, cluster); // get next cluster + } + + // update position fields + seek_hint->cluster = cluster; + seek_hint->sector_of_cluster = delta_sector_from_clusters_first_sector % ctrl->sectors_per_cluster; + seek_hint->sector += sector_boundaries_crossed; + seek_hint->pos = pos; + seek_hint->sector_begin_pos += sector_boundaries_crossed * ctrl->bytes_per_sector; + } + } + } else { // if seeking backwards, then no acceleration is available + const Fat32_FileEntry * entry = fat32_get_file_entry_by_byte_address(ctrl, file_helper->entry_ba); // fetch file entry + uint32_t first_cluster = fat32_get_first_cluster(entry); // get first cluster of the entry + uint32_t cluster = fat32_get_cluster_from_pos(ctrl, first_cluster, pos); // get cluster sequence number of the file using the read position + uint32_t first_sector = fat32_get_first_sector_of_cluster(ctrl, cluster); // get the first sector of the cluster + uint32_t bytes_per_cluster = fat32_get_bytes_per_cluster(ctrl); // get number of bytes per cluster + uint32_t pos_in_cluster = pos % bytes_per_cluster; // transform read position to a relative read position from the cluster beginning + uint32_t sector_of_cluster = pos_in_cluster / ctrl->bytes_per_sector; // get the sequence number of sector in the cluster that will be read + uint32_t sector = first_sector + sector_of_cluster; // offset the sectors based on relative read position + + // update position fields + seek_hint->pos = pos; + seek_hint->sector_of_cluster = sector_of_cluster; + seek_hint->cluster = cluster; + seek_hint->sector = first_sector + sector_of_cluster; + seek_hint->sector_begin_pos = pos - (pos % ctrl->bytes_per_sector); + } + + return 0; +} + +uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p) { + FETCH_MSTG; + + Fat32_SeekHint * seek_hint = &file_helper->last_pos; + + uint32_t pos_in_sector = seek_hint->pos - seek_hint->sector_begin_pos; // determine position in current sector + + READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); + + // 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; + + // reading from a file may involve reading across cluster boundaries + + // 1. read the beginning from the first cluster + uint32_t first_read_len = MIN(read_len, ctrl->bytes_per_sector - seek_hint->pos); + memcpy(p, buffer + pos_in_sector, first_read_len); + read_len -= first_read_len; // decrease the total read len with the number of bytes read from the first sector of the file + offset += first_read_len; // advance offset + seek_hint->pos += first_read_len; // advance seek pos + + // 2. read from intermediate sectors and read from the final sector + + // if the remaining read length is non-zero, then maintain buffered sectors + while (read_len > 0) { + seek_hint->sector_of_cluster++; // increase sector index + if (seek_hint->sector_of_cluster >= ctrl->sectors_per_cluster) { // if we crossed a cluster boundary, then load the next cluster's first sector in chain + seek_hint->cluster = fat32_get_next_chained_cluster(ctrl, seek_hint->cluster); // get next cluster + seek_hint->sector = fat32_get_first_sector_of_cluster(ctrl, seek_hint->cluster); + seek_hint->sector_of_cluster = 0; // it's the zeroth sector of the cluster + READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); // load the newly determined cluster's first sector + } else { // if we didn't cross a cluster boundary, then... + seek_hint->sector++; //...just increase the sector count + READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); // ...and load that sector + } + + seek_hint->sector_begin_pos += ctrl->bytes_per_sector; // advance begin position, we have stepped one sector forward + + // copy the whole sector or some portion of the sector counting from the sector's begin + uint32_t copy_size = MIN(ctrl->bytes_per_sector, read_len); + memcpy(p + offset, buffer, copy_size); + read_len -= copy_size; + offset += copy_size; + seek_hint->pos += copy_size; + } + return read_len; } \ No newline at end of file diff --git a/fs/fat32/fat32.h b/fs/fat32/fat32.h index 3176ca8..3d75cbd 100644 --- a/fs/fat32/fat32.h +++ b/fs/fat32/fat32.h @@ -95,21 +95,29 @@ typedef struct { uint32_t pos; // byte position uint32_t cluster; // cluster corresponding to the position uint32_t sector; // sector of the last read + uint32_t sector_of_cluster; // sequence number of sector in the current cluster uint32_t sector_begin_pos; // byte position of the sector's begin } Fat32_SeekHint; typedef struct { - Fat32_CtrlBlock * ctrl; // pointer FAT32 control block + const Fat32_CtrlBlock * ctrl; // pointer FAT32 control block Fat32_SeekHint last_pos; // last read position - uint32_t entry_pos; // FileEntry byte position + uint32_t entry_ba; // FileEntry byte address on the mass storage + uint32_t size; // file size } Fat32_FileHelper; int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg); int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char * dir); -uint32_t fat32_read_file(const Fat32_CtrlBlock * ctrl, const Fat32_FileEntry * entry, uint32_t pos, uint32_t len, uint8_t * p); +uint32_t fat32_read_file_random_access(const Fat32_CtrlBlock * ctrl, const Fat32_FileEntry * entry, uint32_t pos, uint32_t len, uint8_t * p); -const Fat32_FileEntry * fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path); +const Fat32_FileEntry * fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path, uint32_t * file_entry_ba); + +int fat32_open_file(const Fat32_CtrlBlock *ctrl, const char *path, Fat32_FileHelper *file_helper); + +uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_FileHelper *file_helper); + +uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p); #endif //EMBPART_FAT32_H