1 /*
2 * Copyright (c) 2015 Steve White
3 * Copyright (c) 2022 Travis Geiselbrecht
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9
10 #include <lk/trace.h>
11 #include <endian.h>
12 #include <stdint.h>
13 #include <stdio.h>
14
15 #include "fat_fs.h"
16 #include "fat_priv.h"
17
18 #define LOCAL_TRACE FAT_GLOBAL_TRACE(0)
19
fat_next_cluster_in_chain(fat_fs * fat,uint32_t cluster)20 uint32_t fat_next_cluster_in_chain(fat_fs *fat, uint32_t cluster) {
21 DEBUG_ASSERT(fat->lock.is_held());
22
23 // offset in bytes into the FAT for this entry
24 uint32_t fat_offset;
25 if (fat->info().fat_bits == 32) {
26 fat_offset = cluster * 4;
27 } else if (fat->info().fat_bits == 16) {
28 fat_offset = cluster * 2;
29 } else {
30 fat_offset = cluster + (cluster / 2);
31 }
32 LTRACEF("cluster %#x, fat_offset %u\n", cluster, fat_offset);
33
34 const uint32_t fat_sector = fat_offset / fat->info().bytes_per_sector;
35 uint32_t bnum = fat->info().reserved_sectors + fat_sector;
36 const uint32_t fat_offset_in_sector = fat_offset % fat->info().bytes_per_sector;
37
38 // grab a pointer to the sector holding the fat entry
39 void *cache_ptr;
40 int err = bcache_get_block(fat->bcache(), &cache_ptr, bnum);
41 if (err < 0) {
42 printf("bcache_get_block returned: %i\n", err);
43 return EOF_CLUSTER;
44 }
45
46 uint32_t next_cluster;
47 if (fat->info().fat_bits == 32) {
48 const auto *table = (const uint32_t *)cache_ptr;
49 const auto index = fat_offset_in_sector / 4;
50 next_cluster = table[index];
51 LE32SWAP(next_cluster);
52
53 // mask out the top nibble
54 next_cluster &= 0x0fffffff;
55 } else if (fat->info().fat_bits == 16) {
56 const auto *table = (const uint16_t *)cache_ptr;
57 const auto index = fat_offset_in_sector / 2;
58 next_cluster = table[index];
59 LE16SWAP(next_cluster);
60
61 // if it's a EOF 16 bit entry, extend it so that it looks to be 32bit
62 if (next_cluster > 0xfff0) {
63 next_cluster |= 0x0fff0000;
64 }
65 } else { // fat12
66 DEBUG_ASSERT(fat->info().fat_bits == 12);
67 DEBUG_ASSERT(fat_offset_in_sector < fat->info().bytes_per_sector);
68
69 if (fat_offset_in_sector != (fat->info().bytes_per_sector - 1)) {
70 // normal, non sector straddling logic
71 next_cluster = fat_read16(cache_ptr, fat_offset_in_sector);
72 } else {
73 // need to straddle a fat sector
74
75 // read the first byte of the entry
76 next_cluster = ((const uint8_t *)cache_ptr)[fat_offset_in_sector];
77
78 // close the block cache and open the next sector
79 bcache_put_block(fat->bcache(), bnum);
80 bnum++;
81 err = bcache_get_block(fat->bcache(), &cache_ptr, bnum);
82 if (err < 0) {
83 printf("bcache_get_block returned: %i\n", err);
84 return EOF_CLUSTER;
85 }
86
87 // read the second byte
88 next_cluster |= ((const uint8_t *)cache_ptr)[0] << 8;
89 }
90
91 // odd cluster, shift over to get our value
92 if (cluster & 1) {
93 next_cluster >>= 4;
94 } else {
95 next_cluster &= 0x0fff;
96 }
97
98 // if it's a EOF 12 bit entry, extend it so that it looks to be 32bit
99 if (next_cluster > 0xff0) {
100 next_cluster |= 0x0ffff000;
101 }
102 }
103
104 // return the sector to the block cache
105 bcache_put_block(fat->bcache(), bnum);
106
107 LTRACEF("returning cluster %#x\n", next_cluster);
108
109 return next_cluster;
110 }
111
112 // given a starting fat cluster, walk the fat chain for offset bytes, returning a new cluster or end of file
file_offset_to_cluster(fat_fs * fat,uint32_t start_cluster,off_t offset)113 uint32_t file_offset_to_cluster(fat_fs *fat, uint32_t start_cluster, off_t offset) {
114 DEBUG_ASSERT(fat->lock.is_held());
115
116 // negative offsets do not make sense
117 DEBUG_ASSERT(offset >= 0);
118 if (offset < 0) {
119 return EOF_CLUSTER;
120 }
121
122 // starting at the start cluster, walk forward N clusters, based on how far
123 // the offset is units of cluster bytes
124 uint32_t found_cluster = start_cluster;
125 size_t clusters_to_walk = (size_t)offset / fat->info().bytes_per_cluster;
126 while (clusters_to_walk > 0) {
127 // walk foward these many clusters, returning the FAT entry at that spot
128 found_cluster = fat_next_cluster_in_chain(fat, found_cluster);
129 if (is_eof_cluster(found_cluster)) {
130 break;
131 }
132 clusters_to_walk--;
133 }
134
135 return found_cluster;
136 }
137
fat_sector_for_cluster(fat_fs * fat,uint32_t cluster)138 uint32_t fat_sector_for_cluster(fat_fs *fat, uint32_t cluster) {
139 DEBUG_ASSERT(fat->lock.is_held());
140
141 // cluster 0 and 1 are undefined
142 DEBUG_ASSERT(cluster >= 2);
143 DEBUG_ASSERT(cluster < fat->info().total_clusters);
144 if (cluster >= fat->info().total_clusters) {
145 return 0;
146 }
147
148 uint32_t sector = fat->info().data_start_sector + (cluster - 2) * fat->info().sectors_per_cluster;
149
150 DEBUG_ASSERT(sector < fat->info().total_sectors);
151
152 return sector;
153 }
154
fat_read_cluster(fat_fs * fat,void * buf,uint32_t cluster)155 ssize_t fat_read_cluster(fat_fs *fat, void *buf, uint32_t cluster) {
156 DEBUG_ASSERT(fat->lock.is_held());
157
158 LTRACEF("buf %p, cluster %u\n", buf, cluster);
159
160 auto sector = fat_sector_for_cluster(fat, cluster);
161 return bio_read_block(fat->dev(), buf, sector, fat->info().sectors_per_cluster);
162 }
163
164
165