#include #include int fatfs_get_cluster_from_index(fatfs_t *fs, fatnode_t *node, uintmax_t index, fatfs_cluster_t *cluster) { if (node->saved_cluster == 0 || node->saved_index > index) { node->saved_cluster = node->cluster; node->saved_index = 0; } fatfs_cluster_t iterator = node->saved_cluster; // empty file? if (iterator == 0) { // return EOF switch (fs->type) { case FATFS_FAT12: *cluster = FATFS_CLUSTER_EOF_FAT12; break; case FATFS_FAT16: *cluster = FATFS_CLUSTER_EOF_FAT16; break; case FATFS_FAT32: *cluster = FATFS_CLUSTER_EOF_FAT32; break; } return 0; } while (index--) { int error = fatfs_next_cluster(fs, iterator, &iterator); if (error) return error; // we set iterator to the specific EOF cluster because many different values can // signal an EOF (example: 0xff8 to 0xfff for fat12) if (fs->type == FATFS_FAT12 && FATFS_CLUSTER_IS_EOF_FAT12(iterator)) { iterator = FATFS_CLUSTER_EOF_FAT12; break; } else if (fs->type == FATFS_FAT16 && FATFS_CLUSTER_IS_EOF_FAT16(iterator)) { iterator = FATFS_CLUSTER_EOF_FAT16; break; } else if (fs->type == FATFS_FAT32 && FATFS_CLUSTER_IS_EOF_FAT32(iterator)) { iterator = FATFS_CLUSTER_EOF_FAT32; break; } } if (fatfs_is_cluster_eof(fs, iterator) == false) { // update the last found index node->saved_index = index; node->saved_cluster = iterator; } *cluster = iterator; return 0; } static inline size_t cluster_disk_offset(fatfs_t *fs, fatfs_cluster_t cluster) { return fs->data_offset + (cluster - 2) * fs->cluster_size; } int fatfs_get_file_disk_offset(fatfs_t *fs, fatnode_t *node, size_t byte_offset, size_t *disk_offset) { __assert(byte_offset < node->size); fatfs_cluster_t cluster; int error = fatfs_get_cluster_from_index(fs, node, byte_offset / fs->cluster_size, &cluster); if (error) return error; *disk_offset = cluster_disk_offset(fs, cluster) + byte_offset % fs->cluster_size; return 0; } static int fatfs_abc_rw_iovec(fatfs_t *fs, iovec_iterator_t *iovec_iterator, size_t count, uintmax_t offset, bool write) { while (count) { size_t block_offset = offset % fs->abc.block_size; size_t do_count = min(count, fs->abc.block_size - block_offset); abc_block_t *block; int error = abc_get_block(&fs->abc, offset / fs->abc.block_size, &block); if (error) return error; void *buffer = (void *)((uintptr_t)block->data + block_offset); error = write ? iovec_iterator_copy_to_buffer(iovec_iterator, buffer, do_count) : iovec_iterator_copy_from_buffer(iovec_iterator, buffer, do_count); if (error == 0 && write) abc_make_dirty(&fs->abc, block); abc_release_block(&fs->abc, block); if (error) return error; offset += do_count; count -= do_count; } return 0; } int fatfs_disk_rw_iovec(fatfs_t *fs, iovec_iterator_t *iovec_iterator, size_t count, uintmax_t offset, bool write, bool cache) { if (cache) return fatfs_abc_rw_iovec(fs, iovec_iterator, count, offset, write); size_t done_count; int error = write ? vfs_write_iovec(fs->backing, iovec_iterator, count, offset, &done_count, V_FFLAGS_NOCACHE) : vfs_read_iovec(fs->backing, iovec_iterator, count, offset, &done_count, V_FFLAGS_NOCACHE); if (error) return error; __assert(done_count == count); return 0; } int fatfs_disk_rw(fatfs_t *fs, void *buffer, size_t count, uintmax_t offset, bool write, bool cache) { iovec_t iovec = { .addr = buffer, .len = count }; iovec_iterator_t iovec_iterator; iovec_iterator_init(&iovec_iterator, &iovec, 1); return fatfs_disk_rw_iovec(fs, &iovec_iterator, count, offset, write, cache); } int fatfs_rw_clusters_iovec(fatfs_t *fs, fatnode_t *node, iovec_iterator_t *iovec_iterator, size_t count, uintmax_t index, bool write, bool cache) { fatfs_cluster_t cluster; int error = fatfs_get_cluster_from_index(fs, node, index, &cluster); if (error) return error; __assert(cluster && fatfs_is_cluster_eof(fs, cluster) == false); uintmax_t i = 0; while (i < count) { __assert(index + i < ROUND_UP(node->size, fs->cluster_size) / fs->cluster_size); size_t cluster_count = 1; fatfs_cluster_t last_cluster = cluster; fatfs_cluster_t next_loop_cluster = 0; while (i + cluster_count < count) { fatfs_cluster_t next_cluster; error = fatfs_next_cluster(fs, last_cluster, &next_cluster); if (error) return error; __assert(next_cluster); __assert(fatfs_is_cluster_eof(fs, next_cluster) == false); if (next_cluster != last_cluster + 1) { next_loop_cluster = next_cluster; break; } last_cluster = next_cluster; cluster_count += 1; } uintmax_t disk_offset = cluster_disk_offset(fs, cluster); size_t byte_count = cluster_count * fs->cluster_size; error = fatfs_disk_rw_iovec(fs, iovec_iterator, byte_count, disk_offset, write, cache); if (error) return error; i += cluster_count; cluster = next_loop_cluster; } return 0; } int fatfs_rw_cluster_iovec(fatfs_t *fs, fatnode_t *node, iovec_iterator_t *iovec_iterator, size_t count, uintmax_t offset, uintmax_t index, bool write, bool cache) { __assert(offset + count <= fs->cluster_size); __assert(index < ROUND_UP(node->size, fs->cluster_size) / fs->cluster_size); fatfs_cluster_t cluster; int error = fatfs_get_cluster_from_index(fs, node, index, &cluster); if (error) return error; __assert(cluster && fatfs_is_cluster_eof(fs, cluster) == false); uintmax_t disk_offset = cluster_disk_offset(fs, cluster) + offset; return fatfs_disk_rw_iovec(fs, iovec_iterator, count, disk_offset, write, cache); } int fatfs_rw_bytes_iovec(fatfs_t *fs, fatnode_t *node, iovec_iterator_t *iovec_iterator, size_t count, uintmax_t offset, bool write, bool cache) { uintmax_t index = offset / fs->cluster_size; uintmax_t start_offset = offset % fs->cluster_size; // r/w the first cluster if there's an offset into it if (start_offset) { size_t block_remaining = fs->cluster_size - start_offset; size_t do_count = min(block_remaining, count); int error = fatfs_rw_cluster_iovec(fs, node, iovec_iterator, do_count, start_offset, index, write, cache); if (error) return error; count -= do_count; index += 1; } // r/w all middle clusters size_t blocks = count / fs->cluster_size; if (blocks) { int error = fatfs_rw_clusters_iovec(fs, node, iovec_iterator, blocks, index, write, cache); if (error) return error; count -= blocks * fs->cluster_size; index += blocks; } // r/w remaining cluster if there's any data left and return return count ? fatfs_rw_cluster_iovec(fs, node, iovec_iterator, count, 0, index, write, cache) : 0; } int fatfs_rw_bytes(fatfs_t *fs, fatnode_t *node, void *buffer, size_t count, uintmax_t offset, bool write, bool cache) { iovec_t iovec = { .addr = buffer, .len = count }; iovec_iterator_t iovec_iterator; iovec_iterator_init(&iovec_iterator, &iovec, 1); return fatfs_rw_bytes_iovec(fs, node, &iovec_iterator, count, offset, write, cache); }