1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** FileX Component */
16 /** */
17 /** Fault Tolerant */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define FX_SOURCE_CODE
23
24 #include "fx_api.h"
25 #include "fx_utility.h"
26 #include "fx_fault_tolerant.h"
27
28
29 #ifdef FX_ENABLE_FAULT_TOLERANT
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _fx_utility_FAT_entry_multiple_sectors_check PORTABLE C */
37 /* 6.2.0 */
38 /* AUTHOR */
39 /* */
40 /* Tiejun Zhou, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function checks whether the FAT entry spans multiple sectors. */
45 /* It is only possible when the file system is FAT12. */
46 /* */
47 /* INPUT */
48 /* */
49 /* media_ptr Media control block pointer */
50 /* cluster Cluster entry number */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* FX_TRUE FAT entry spans two sectors */
55 /* FX_FALSE FAT entry in one sector */
56 /* */
57 /* CALLS */
58 /* */
59 /* None */
60 /* */
61 /* CALLED BY */
62 /* */
63 /* _fx_fault_tolerant_cleanup_FAT_chain */
64 /* */
65 /* RELEASE HISTORY */
66 /* */
67 /* DATE NAME DESCRIPTION */
68 /* */
69 /* 10-31-2022 Tiejun Zhou Initial Version 6.2.0 */
70 /* */
71 /**************************************************************************/
_fx_utility_FAT_entry_multiple_sectors_check(FX_MEDIA * media_ptr,ULONG cluster)72 static UINT _fx_utility_FAT_entry_multiple_sectors_check(FX_MEDIA *media_ptr, ULONG cluster)
73 {
74 ULONG byte_offset;
75 ULONG FAT_sector;
76
77 /* Check FAT format. */
78 if (!media_ptr -> fx_media_12_bit_FAT)
79 {
80
81 /* Not FAT12. The FAT entry will be in one sector. */
82 return(FX_FALSE);
83 }
84
85 /* File system is FAT12. */
86 /* Calculate the byte offset to the cluster entry. */
87 byte_offset = (((ULONG)cluster << 1) + cluster) >> 1;
88
89 /* Calculate the FAT sector the requested FAT entry resides in. */
90 FAT_sector = (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
91 (ULONG)media_ptr -> fx_media_reserved_sectors;
92
93 /* Now calculate the byte offset into this FAT sector. */
94 byte_offset = byte_offset -
95 ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
96 media_ptr -> fx_media_bytes_per_sector);
97
98 /* Determine if we are now past the end of the FAT buffer in memory. */
99 if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1))
100 {
101
102 /* Yes, we are past the end of the FAT buffer. */
103 return(FX_TRUE);
104 }
105 else
106 {
107
108 /* No, we are not past the end of the FAT buffer. */
109 return(FX_FALSE);
110 }
111 }
112
113 /**************************************************************************/
114 /* */
115 /* FUNCTION RELEASE */
116 /* */
117 /* _fx_fault_tolerant_cleanup_FAT_chain PORTABLE C */
118 /* 6.2.0 */
119 /* AUTHOR */
120 /* */
121 /* William E. Lamie, Microsoft Corporation */
122 /* */
123 /* DESCRIPTION */
124 /* */
125 /* This function cleans up FAT chain. In order to prevent failures */
126 /* during the link-list walk, and to improve efficiency, this function */
127 /* frees the FAT entry chain in sections. For each section, it frees */
128 /* FAT entry from the last entry in the chain and works backwards. */
129 /* Using this method, if the system fails in the middle of the free */
130 /* operation, the head is preserved, and fault tolerant module can */
131 /* "redo" this operation next time it starts up. */
132 /* */
133 /* INPUT */
134 /* */
135 /* media_ptr Media control block pointer */
136 /* operation Recover a failure operation or*/
137 /* cleanup old FAT chain */
138 /* */
139 /* OUTPUT */
140 /* */
141 /* return status */
142 /* */
143 /* CALLS */
144 /* */
145 /* _fx_utility_FAT_entry_read Read a FAT entry */
146 /* _fx_utility_FAT_entry_write Write a FAT entry */
147 /* _fx_utility_16_unsigned_write Write a USHORT from memory */
148 /* _fx_utility_32_unsigned_write Write a ULONG from memory */
149 /* _fx_utility_32_unsigned_read Read a ULONG from memory */
150 /* _fx_utility_FAT_flush Flush written FAT entries */
151 /* _fx_fault_tolerant_calculate_checksum Compute Checksum of data */
152 /* _fx_fault_tolerant_write_log_file Write log file */
153 /* _fx_utility_FAT_entry_multiple_sectors_check */
154 /* Check sectors for FAT entry */
155 /* */
156 /* CALLED BY */
157 /* */
158 /* _fx_fault_tolerant_apply_logs */
159 /* _fx_fault_tolerant_recover */
160 /* */
161 /* RELEASE HISTORY */
162 /* */
163 /* DATE NAME DESCRIPTION */
164 /* */
165 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
166 /* 09-30-2020 William E. Lamie Modified comment(s), */
167 /* resulting in version 6.1 */
168 /* 10-31-2022 Tiejun Zhou Modified comment(s), */
169 /* fixed FAT entry span two */
170 /* sectors for FAT12, */
171 /* resulting in version 6.2.0 */
172 /* */
173 /**************************************************************************/
_fx_fault_tolerant_cleanup_FAT_chain(FX_MEDIA * media_ptr,UINT operation)174 UINT _fx_fault_tolerant_cleanup_FAT_chain(FX_MEDIA *media_ptr, UINT operation)
175 {
176 UINT status;
177 ULONG current_cluster;
178 ULONG next_cluster = 0;
179 ULONG head_cluster;
180 ULONG tail_cluster;
181 ULONG cache_count;
182 ULONG cache_max = FX_FAULT_TOLERANT_CACHE_SIZE >> 2;
183 ULONG *cache_ptr = media_ptr -> fx_media_fault_tolerant_cache;
184 ULONG next_session;
185 USHORT checksum;
186 ULONG i;
187 FX_FAULT_TOLERANT_FAT_CHAIN *FAT_chain;
188 ULONG FAT_sector;
189 ULONG last_FAT_sector;
190
191 /* Set FAT chain pointer. */
192 FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer + FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET);
193
194 /* Get head and tail cluster. */
195 if (operation == FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER)
196 {
197
198 /* Recover phase. Cleanup (remove) new FAT chain. */
199 head_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new);
200 }
201 else
202 {
203
204 /* Cleanup phase. Cleanup (remove) old FAT chain. */
205 head_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original);
206 }
207
208 /* At this point, the head_cluster points to the cluster chain that is to be removed. */
209
210 /* Tail cluster points to the back of the origianal FAT chain where the new chain is attached to.
211 The remove process terminates once this cluster is encountered. */
212 tail_cluster = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_insertion_back);
213
214 /* Are there any clusters to free? */
215 if ((head_cluster < FX_FAT_ENTRY_START) || (head_cluster >= media_ptr -> fx_media_fat_reserved))
216 {
217 return(FX_SUCCESS);
218 }
219
220 /* To optimize the deletion process of a long FAT chain, the deletion is done in sessions. During one session,
221 a set of FAT entries are removed. If one session is interrupted, after restart the deletion process resumes and
222 picks up from where it left.
223
224 During one deletion session, all FAT entries are between head_cluster(inclusive) and next_session (exclusive).
225 */
226 next_session = _fx_utility_32_unsigned_read((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion);
227
228 /* Loop to cleanup all FAT entries. */
229 while ((head_cluster >= FX_FAT_ENTRY_START) && (head_cluster < media_ptr -> fx_media_fat_reserved))
230 {
231
232 /* Initialize. */
233 current_cluster = head_cluster;
234 cache_count = 0;
235
236 /* Loop from head cluster to find entries to cleanup and store this information into cache. */
237 do
238 {
239
240 /* Get the next FAT entry. */
241 status = _fx_utility_FAT_entry_read(media_ptr, current_cluster, &next_cluster);
242 if (status != FX_SUCCESS)
243 {
244
245 /* Return the error status. */
246 return(status);
247 }
248
249 /* Whether or not the cluster is occupied. */
250 if (next_cluster == FX_FREE_CLUSTER)
251 {
252
253 /* Current cluster has already been released. */
254 break;
255 }
256
257 /* Cache the FAT list. */
258 cache_ptr[cache_count++] = current_cluster;
259
260 /* Check whether FAT entry spans multiple sectors. */
261 if (_fx_utility_FAT_entry_multiple_sectors_check(media_ptr, current_cluster))
262 {
263 if (head_cluster == next_session || next_session == FX_FREE_CLUSTER)
264 {
265 next_session = next_cluster;
266 }
267 break;
268 }
269
270 /* Move to next cluster. */
271 current_cluster = next_cluster;
272 } while ((next_cluster >= FX_FAT_ENTRY_START) &&
273 (next_cluster < media_ptr -> fx_media_fat_reserved) &&
274 (next_cluster != tail_cluster) &&
275 (cache_count < cache_max));
276
277 /* Get next session. */
278 if (cache_count == cache_max)
279 {
280 next_session = next_cluster;
281 }
282
283 /* Update head cluster and next session into log file. */
284 _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_next_deletion, next_session);
285 if (operation == FX_FAULT_TOLERANT_FAT_CHAIN_RECOVER)
286 {
287 _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_new, head_cluster);
288 }
289 else
290 {
291 _fx_utility_32_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_head_original, head_cluster);
292 }
293
294 /* Update checksum. */
295 _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, 0);
296 checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)FAT_chain, FX_FAULT_TOLERANT_FAT_CHAIN_SIZE);
297 _fx_utility_16_unsigned_write((UCHAR *)&FAT_chain -> fx_fault_tolerant_FAT_chain_checksumm, checksum);
298
299 /* Flush the fault tolerant log into the file system. */
300 _fx_fault_tolerant_write_log_file(media_ptr, 0);
301
302 /* Loop to free FAT in cache from back to front. */
303 if (cache_count > 0)
304 {
305
306 /* Get sector of last FAT entry. */
307 last_FAT_sector = _fx_utility_FAT_sector_get(media_ptr, cache_ptr[cache_count - 1]);
308 i = cache_count;
309 while (i > 0)
310 {
311 i--;
312
313 /* Get sector of current FAT entry. */
314 FAT_sector = _fx_utility_FAT_sector_get(media_ptr, cache_ptr[i]);
315 if (FAT_sector != last_FAT_sector)
316 {
317
318 /* Current FAT entry is located in different sector. Force flush. */
319 /* Flush the cached individual FAT entries */
320 _fx_utility_FAT_flush(media_ptr);
321
322 /* Update sector of current FAT entry. */
323 last_FAT_sector = FAT_sector;
324 }
325
326 /* Free current_cluster. */
327 status = _fx_utility_FAT_entry_write(media_ptr, cache_ptr[i], FX_FREE_CLUSTER);
328 if (status != FX_SUCCESS)
329 {
330
331 /* Return the error status. */
332 return(status);
333 }
334
335 }
336
337 /* Increase the available clusters in the media control block. */
338 media_ptr -> fx_media_available_clusters += cache_count;
339 }
340
341 /* Flush the cached individual FAT entries */
342 _fx_utility_FAT_flush(media_ptr);
343
344
345 /* Get head cluster. */
346 if (head_cluster == next_session)
347 {
348 break;
349 }
350 head_cluster = next_session;
351 }
352
353 return(FX_SUCCESS);
354 }
355 #endif /* FX_ENABLE_FAULT_TOLERANT */
356