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)24 file_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()39 file_block_iterator::~file_block_iterator() {
40     put_bcache_block();
41 }
42 
43 // move N sectors ahead
next_sectors(uint32_t sectors)44 status_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()73 status_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()90 void 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)98 status_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