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