1 /* 2 * Copyright (c) 2022 Travis Geiselbrecht 3 * 4 * Use of this source code is governed by a MIT-style 5 * license that can be found in the LICENSE file or at 6 * https://opensource.org/licenses/MIT 7 */ 8 #include "file_iterator.h" 9 10 #include <lk/cpp.h> 11 #include <lk/err.h> 12 #include <lk/trace.h> 13 #include <endian.h> 14 #include <stdint.h> 15 #include <stdlib.h> 16 #include <stdio.h> 17 #include <string.h> 18 19 #include "fat_fs.h" 20 #include "fat_priv.h" 21 22 #define LOCAL_TRACE FAT_GLOBAL_TRACE(0) 23 file_block_iterator(fat_fs * _fat,uint32_t starting_cluster)24file_block_iterator::file_block_iterator(fat_fs *_fat, uint32_t starting_cluster) : fat(_fat) { 25 if (starting_cluster == 0) { 26 // special case on fat12/16 to represent the root dir. 27 // load 0 into cluster and use sector_offset as relative to the 28 // start of the volume. 29 DEBUG_ASSERT(fat->info().fat_bits == 12 || fat->info().fat_bits == 16); 30 DEBUG_ASSERT(fat->info().root_start_sector != 0); 31 cluster = 0; // from now on out, cluster 0 will represent the root dir 32 sector_offset = fat->info().root_start_sector; 33 } else { 34 cluster = starting_cluster; 35 sector_offset = 0; 36 } 37 } 38 ~file_block_iterator()39file_block_iterator::~file_block_iterator() { 40 put_bcache_block(); 41 } 42 43 // move N sectors ahead next_sectors(uint32_t sectors)44status_t file_block_iterator::next_sectors(uint32_t sectors) { 45 if (cluster == 0) { 46 // on linear dirs it's just incrementing sectors 47 sector_offset += sectors; 48 // are we at the end of the root dir? 49 if (sector_offset >= fat->info().root_start_sector + fat->info().root_dir_sectors) { 50 return ERR_OUT_OF_RANGE; 51 } 52 } else { 53 // for non linear dirs we step sector by sector into clusters 54 // and then wrap to the next cluster. 55 for (uint32_t i = 0; i < sectors; i++) { 56 sector_offset++; 57 if (sector_offset == fat->info().sectors_per_cluster) { 58 sector_offset = 0; 59 60 cluster = fat_next_cluster_in_chain(fat, cluster); 61 if (is_eof_cluster(cluster)) { 62 return ERR_OUT_OF_RANGE; 63 } 64 } 65 } 66 } 67 // track the number of sectors we've moved forward 68 sector_inc_count += sectors; 69 70 return load_current_bcache_block(); 71 } 72 load_current_bcache_block()73status_t file_block_iterator::load_current_bcache_block() { 74 // close the current bcache 75 put_bcache_block(); 76 77 if (cluster == 0) { 78 // we're in a linear root dir, the sector_offset variable represents the raw sector number 79 return load_bcache_block(sector_offset); 80 } else { // cluster != 0 81 DEBUG_ASSERT(sector_offset < fat->info().bytes_per_sector); 82 83 // compute the sector we should be on given the cluster and sector_offset 84 auto sector = fat_sector_for_cluster(fat, cluster) + sector_offset; 85 86 return load_bcache_block(sector); 87 } 88 } 89 put_bcache_block()90void file_block_iterator::put_bcache_block() { 91 if (bcache_buf) { 92 bcache_put_block(fat->bcache(), bcache_bnum); 93 bcache_buf = nullptr; 94 bcache_bnum = 0; 95 } 96 } 97 load_bcache_block(bnum_t bnum)98status_t file_block_iterator::load_bcache_block(bnum_t bnum) { 99 DEBUG_ASSERT(!bcache_buf); 100 101 auto err = bcache_get_block(fat->bcache(), &bcache_buf, bnum); 102 if (err >= 0) { 103 DEBUG_ASSERT(bcache_buf); 104 bcache_bnum = bnum; 105 } 106 107 return err; 108 } 109 110