Bugfix filesystem reads at cluster boundary
This commit is contained in:
parent
2402b6f515
commit
8655229548
@ -332,6 +332,10 @@ struct fat32_handle {
|
||||
* @brief the first cluster of this directory or file for convenience purposes
|
||||
*/
|
||||
uint32_t first_cluster;
|
||||
/**
|
||||
* @brief the how manieth cluster in the chain is currently stored in the current_cluster field (0-indexed)
|
||||
*/
|
||||
size_t current_cluster_index_in_chain;
|
||||
/**
|
||||
* @brief cluster containing the current byte offset
|
||||
*
|
||||
|
||||
120
lib/fs/fat32.c
120
lib/fs/fat32.c
@ -856,6 +856,7 @@ static errval_t fat32_create_handle(struct fat32 *fat32, struct fat32_handle **h
|
||||
(*handle)->is_dir = is_dir;
|
||||
(*handle)->first_cluster = first_cluster;
|
||||
(*handle)->current_cluster = first_cluster;
|
||||
(*handle)->current_cluster_index_in_chain = 0;
|
||||
(*handle)->byte_offset = 0;
|
||||
(*handle)->directory_entry_ref = directory_entry_ref;
|
||||
|
||||
@ -879,6 +880,44 @@ static errval_t fat32_free_handle(struct fat32 *fat32, struct fat32_handle *hand
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
static errval_t fat32_handle_update_current_cluster(struct fat32 *fat32, struct fat32_handle *handle, bool extend) {
|
||||
errval_t err;
|
||||
|
||||
// debug_printf("[fat32_handle_update_current_cluster]\n");
|
||||
|
||||
size_t cluster_index = handle->byte_offset / fat32->bytes_per_cluster;
|
||||
|
||||
// debug_printf("[fat32_handle_update_current_cluster] target cluster %lu for offset %lu\n", cluster_index, handle->byte_offset);
|
||||
|
||||
if (handle->current_cluster_index_in_chain == cluster_index) return SYS_ERR_OK;
|
||||
|
||||
// if we are too far in the chain then we have to re-resolve from the start
|
||||
if (handle->current_cluster_index_in_chain > cluster_index) {
|
||||
handle->current_cluster_index_in_chain = 0;
|
||||
handle->current_cluster = handle->first_cluster;
|
||||
}
|
||||
|
||||
// advance in the cluster chain until we reach the desired index or run out of clusters
|
||||
assert(!fat32_is_end_of_cluster_chain(handle->current_cluster));
|
||||
while(handle->current_cluster_index_in_chain < cluster_index) {
|
||||
if (extend) {
|
||||
err = fat32_next_cluster_in_chain_or_extend(fat32, handle->current_cluster, &handle->current_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
assert(!fat32_is_end_of_cluster_chain(handle->current_cluster));
|
||||
} else {
|
||||
uint32_t next_cluster;
|
||||
err = fat32_next_cluster_in_chain(fat32, handle->current_cluster, &next_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
if(fat32_is_end_of_cluster_chain(next_cluster)) return FS_ERR_INDEX_BOUNDS;
|
||||
handle->current_cluster = next_cluster;
|
||||
}
|
||||
++handle->current_cluster_index_in_chain;
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verify contents of the bootsector of the SD Card
|
||||
*
|
||||
@ -1046,7 +1085,7 @@ errval_t fat32_init(
|
||||
fat32->max_cluster_number = ((bpb->total_sector_32_count - fat32->first_data_sector) / bpb->sectors_per_cluster) + 1;
|
||||
fat32->bytes_per_cluster = fat32->bytes_per_sector * fat32->sectors_per_cluster;
|
||||
|
||||
fat32_print(bpb);
|
||||
// fat32_print(bpb);
|
||||
fat32_verify(bpb);
|
||||
|
||||
debug_printf("[fat32_init] verified FAT32\n");
|
||||
@ -1274,6 +1313,10 @@ static errval_t _fat32_readdir(struct fat32 *fat32, struct fat32_handle *dir_han
|
||||
return FS_ERR_INVALID_FH;
|
||||
}
|
||||
|
||||
// follow the chain if the current cluster is not defined
|
||||
err = fat32_handle_update_current_cluster(fat32, dir_handle, false);
|
||||
if(err_is_fail(err)) return err;
|
||||
|
||||
*name = NULL;
|
||||
|
||||
// search for the next file in the directory starting at the current byte offset
|
||||
@ -1302,8 +1345,8 @@ static errval_t _fat32_readdir(struct fat32 *fat32, struct fat32_handle *dir_han
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
}
|
||||
err = fat32_next_cluster_in_chain(fat32, dir_handle->current_cluster, &dir_handle->current_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
err = fat32_handle_update_current_cluster(fat32, dir_handle, false);
|
||||
if(err_is_fail(err)) return err;
|
||||
}
|
||||
|
||||
return FS_ERR_INDEX_BOUNDS;
|
||||
@ -1535,17 +1578,18 @@ errval_t fat32_fopen(struct fat32 *fat32, const char *path, struct fat32_handle
|
||||
}
|
||||
|
||||
static errval_t _fat32_fread(struct fat32 *fat32, struct fat32_handle *file_handle, void *buffer, size_t bytes, size_t *bytes_read) {
|
||||
// debug_printf("[fat32_fread] on %p: %lu bytes from offset %lu\n", file_handle, bytes, file_handle->byte_offset);
|
||||
debug_printf("[fat32_fread] on %p: %lu bytes from offset %lu\n", file_handle, bytes, file_handle->byte_offset);
|
||||
errval_t err;
|
||||
|
||||
if (file_handle == NULL || file_handle->is_dir) return FS_ERR_INVALID_FH;
|
||||
|
||||
struct fat32_directory_entry directory_entry;
|
||||
// debug_printf("[fat32_fread] read directory entry\n");
|
||||
err = fat32_read_directory_entry(fat32, file_handle->directory_entry_ref, &directory_entry);
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
// trying to read outside of the file
|
||||
if (file_handle->byte_offset >= directory_entry.file_size) {
|
||||
if (bytes == 0 || file_handle->byte_offset >= directory_entry.file_size) {
|
||||
*bytes_read = 0;
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
@ -1558,23 +1602,14 @@ static errval_t _fat32_fread(struct fat32 *fat32, struct fat32_handle *file_hand
|
||||
// debug_printf("[fat32_fread] attempted to read over end of file, reading only %lu/%lu\n", bytes_to_read, bytes);
|
||||
// }
|
||||
|
||||
// follow the chain if the current cluster is not defined
|
||||
if (file_handle->current_cluster == 0) {
|
||||
size_t cluster_min_count = ROUND_UP(file_handle->byte_offset, fat32->bytes_per_cluster) / fat32->bytes_per_cluster;
|
||||
|
||||
// update the current_cluster in the file handle without extending the chain
|
||||
file_handle->current_cluster = file_handle->first_cluster;
|
||||
for (size_t i = 0; i + 1 < cluster_min_count; ++i) {
|
||||
err = fat32_next_cluster_in_chain(fat32, file_handle->current_cluster, &file_handle->current_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
}
|
||||
assert(!fat32_is_end_of_cluster_chain(file_handle->current_cluster));
|
||||
}
|
||||
// make sure we have the correct current cluster
|
||||
err = fat32_handle_update_current_cluster(fat32, file_handle, false);
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
// read until we have reached the desired amount of bytes
|
||||
// where we read is determined by the current byte offset of the file handle
|
||||
*bytes_read = 0;
|
||||
while (*bytes_read < bytes_to_read) {
|
||||
while (true) {
|
||||
// make sure we never try to look at the reserved clusters during traversal
|
||||
assert(file_handle->current_cluster > 1);
|
||||
assert(!fat32_is_end_of_cluster_chain(file_handle->current_cluster));
|
||||
@ -1591,6 +1626,7 @@ static errval_t _fat32_fread(struct fat32 *fat32, struct fat32_handle *file_hand
|
||||
size_t offset_in_sector = file_handle->byte_offset % fat32->bytes_per_sector;
|
||||
size_t bytes_left_in_sector = FAT32_BLOCK_SIZE - offset_in_sector;
|
||||
size_t bytes_in_sector = MIN(bytes_left_in_sector, bytes_to_read - *bytes_read);
|
||||
// debug_printf("[fat32_fread] reading %lu from sector %u due to offset %u\n", bytes_in_sector, current_sector, sector_offset);
|
||||
err = fat32->read_object_fn(current_sector, offset_in_sector, bytes_in_sector, (buffer + *bytes_read));
|
||||
if (err_is_fail(err)) return err;
|
||||
|
||||
@ -1603,15 +1639,13 @@ static errval_t _fat32_fread(struct fat32 *fat32, struct fat32_handle *file_hand
|
||||
}
|
||||
}
|
||||
// continue in the next cluster
|
||||
err = fat32_next_cluster_in_chain(fat32, file_handle->current_cluster, &file_handle->current_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
err = fat32_handle_update_current_cluster(fat32, file_handle, false);
|
||||
if(err_is_fail(err)) return err;
|
||||
// we have checked in advance that this exists by checking the file size
|
||||
assert(!fat32_is_end_of_cluster_chain(file_handle->current_cluster));
|
||||
}
|
||||
|
||||
// debug_printf("[fat32_fread] file now at offset %lu\n", file_handle->byte_offset);
|
||||
|
||||
return SYS_ERR_OK;
|
||||
USER_PANIC("Unreachable code");
|
||||
}
|
||||
errval_t fat32_fread(struct fat32 *fat32, struct fat32_handle *file_handle, void *buffer, size_t bytes, size_t *bytes_read) {
|
||||
errval_t err;
|
||||
@ -1640,21 +1674,8 @@ static errval_t _fat32_fwrite(struct fat32 *fat32, struct fat32_handle *file_han
|
||||
return FS_ERR_WRITE;
|
||||
}
|
||||
|
||||
// if the current position in the file is past the end we need to increase the file size accordingly
|
||||
if (file_handle->byte_offset >= directory_entry.file_size || file_handle->current_cluster == 0) {
|
||||
// debug_printf("Invalid current cluster %u, updating it: %u (%lu)\n", file_handle->current_cluster, file_handle->first_cluster, file_handle->byte_offset);
|
||||
// find the cluster containing the current position in the file
|
||||
size_t cluster_min_count = ROUND_UP(file_handle->byte_offset, fat32->bytes_per_cluster) / fat32->bytes_per_cluster;
|
||||
|
||||
// when we are past the end of the file then the cluster_number is invalid
|
||||
file_handle->current_cluster = file_handle->first_cluster;
|
||||
for (size_t i = 0; i + 1 < cluster_min_count; ++i) {
|
||||
// add a cluster to the chain if necessary
|
||||
err = fat32_next_cluster_in_chain_or_extend(fat32, file_handle->current_cluster, &file_handle->current_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
}
|
||||
// debug_printf("current_cluster: %u\n", file_handle->current_cluster);
|
||||
};
|
||||
// make sure we have a valid current cluster
|
||||
err = fat32_handle_update_current_cluster(fat32, file_handle, true);
|
||||
|
||||
// write the data to the file
|
||||
*bytes_written = 0;
|
||||
@ -1688,10 +1709,11 @@ static errval_t _fat32_fwrite(struct fat32 *fat32, struct fat32_handle *file_han
|
||||
if (err_is_fail(err)) break;
|
||||
if (*bytes_written == bytes) break;
|
||||
|
||||
// if we reached the end of the file just extend it
|
||||
err = fat32_next_cluster_in_chain_or_extend(fat32, file_handle->current_cluster, &file_handle->current_cluster);
|
||||
if (err_is_fail(err)) break;
|
||||
}
|
||||
// continue in the next cluster
|
||||
err = fat32_handle_update_current_cluster(fat32, file_handle, true);
|
||||
if(err_is_fail(err)) return err;
|
||||
// we extend the chain if we reach the end so we should never teach the end of the chain
|
||||
assert(!fat32_is_end_of_cluster_chain(file_handle->current_cluster)); }
|
||||
|
||||
// if we had to increase the file size write it in the directory
|
||||
if (directory_entry.file_size < file_handle->byte_offset) {
|
||||
@ -1791,20 +1813,6 @@ static errval_t _fat32_seek(struct fat32 *fat32, struct fat32_handle *file_handl
|
||||
|
||||
file_handle->byte_offset = absolute_offset;
|
||||
|
||||
// find the cluster containing the current position in the file
|
||||
size_t cluster_min_count = ROUND_UP(file_handle->byte_offset, fat32->bytes_per_cluster) / fat32->bytes_per_cluster;
|
||||
|
||||
// update the current_cluster in the file handle without extending the chain
|
||||
file_handle->current_cluster = file_handle->first_cluster;
|
||||
for (size_t i = 0; i + 1 < cluster_min_count && !fat32_is_end_of_cluster_chain(file_handle->current_cluster); ++i) {
|
||||
err = fat32_next_cluster_in_chain(fat32, file_handle->current_cluster, &file_handle->current_cluster);
|
||||
if (err_is_fail(err)) return err;
|
||||
}
|
||||
// we are over the end of the cluster chain, so invalidate the current_cluster
|
||||
if (fat32_is_end_of_cluster_chain(file_handle->current_cluster)) {
|
||||
file_handle->current_cluster = 0;
|
||||
}
|
||||
|
||||
return SYS_ERR_OK;
|
||||
}
|
||||
errval_t fat32_seek(struct fat32 *fat32, struct fat32_handle *file_handle, enum fs_seekpos whence, off_t offset) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user