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