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_directory.h"
27 #include "fx_fault_tolerant.h"
28 
29 
30 #ifdef FX_ENABLE_FAULT_TOLERANT
31 /**************************************************************************/
32 /*                                                                        */
33 /*  FUNCTION                                               RELEASE        */
34 /*                                                                        */
35 /*    _fx_fault_tolerant_enable                           PORTABLE C      */
36 /*                                                           6.2.0        */
37 /*  AUTHOR                                                                */
38 /*                                                                        */
39 /*    William E. Lamie, Microsoft Corporation                             */
40 /*                                                                        */
41 /*  DESCRIPTION                                                           */
42 /*                                                                        */
43 /*    This function enables the FileX Fault Tolerant feature. It first    */
44 /*    searches for a valid log file.  A valid log file indicates the      */
45 /*    previous write operation failed, and appropriate action now must    */
46 /*    be taken to restore the integrity of the file system.  Once the     */
47 /*    recovery effort is completed, the file system is properly restored. */
48 /*    An empty log file indicates the previous write operation was        */
49 /*    successfully completed and no action needs to be taken at this      */
50 /*    point.  If the file system does not have a log file, or the         */
51 /*    checksum is not valid, it is an indication either the file system   */
52 /*    is not under the protection of FileX Fault Tolerant.  A new log     */
53 /*    file is created.                                                    */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    media_ptr                             Media control block pointer   */
58 /*    memory_buffer                         Pointer to memory buffer.     */
59 /*    memory_size                           Size of memory buffer.        */
60 /*                                                                        */
61 /*  OUTPUT                                                                */
62 /*                                                                        */
63 /*    return status                                                       */
64 /*                                                                        */
65 /*  CALLS                                                                 */
66 /*                                                                        */
67 /*    _fx_fault_tolerant_calculate_checksum Compute Checksum of data      */
68 /*    _fx_fault_tolerant_apply_logs         Apply logs into file system   */
69 /*    _fx_fault_tolerant_recover            Recover FAT chain             */
70 /*    _fx_fault_tolerant_reset_log_file     Reset the log file            */
71 /*    _fx_fault_tolerant_read_log_file      Read log file to cache        */
72 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
73 /*    _fx_utility_16_unsigned_read          Read a USHORT from memory     */
74 /*    _fx_utility_32_unsigned_read          Read a ULONG from memory      */
75 /*                                                                        */
76 /*  CALLED BY                                                             */
77 /*                                                                        */
78 /*    Application Code                                                    */
79 /*                                                                        */
80 /*  RELEASE HISTORY                                                       */
81 /*                                                                        */
82 /*    DATE              NAME                      DESCRIPTION             */
83 /*                                                                        */
84 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
85 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
86 /*                                            resulting in version 6.1    */
87 /*  10-31-2022     Tiejun Zhou              Modified comment(s),          */
88 /*                                            fixed memory buffer when    */
89 /*                                            cache is disabled,          */
90 /*                                            resulting in version 6.2.0  */
91 /*                                                                        */
92 /**************************************************************************/
_fx_fault_tolerant_enable(FX_MEDIA * media_ptr,VOID * memory_buffer,UINT memory_size)93 UINT  _fx_fault_tolerant_enable(FX_MEDIA *media_ptr, VOID *memory_buffer, UINT memory_size)
94 {
95 ULONG                         start_cluster;
96 ULONG                         FAT_value;
97 UINT                          status;
98 ULONG                         checksum;
99 ULONG                         total_size;
100 FX_FAULT_TOLERANT_LOG_HEADER *log_header;
101 FX_FAULT_TOLERANT_FAT_CHAIN  *FAT_chain;
102 ULONG                         cluster_number;
103 ULONG                         i, j;
104 ULONG                         FAT_entry, FAT_sector, FAT_read_sectors;
105 ULONG                         bytes_in_buffer;
106 ULONG                         clusters;
107 ULONG                         bytes_per_sector;
108 ULONG                         bytes_per_cluster;
109 
110     /* Protect against other threads accessing the media.  */
111     FX_PROTECT
112 
113     /* Calculate clusters needed for fault tolerant log. */
114     bytes_per_sector = media_ptr -> fx_media_bytes_per_sector;
115     bytes_per_cluster = bytes_per_sector * media_ptr -> fx_media_sectors_per_cluster;
116 
117     if (bytes_per_cluster == 0)
118     {
119 
120         /* Release media protection.  */
121         FX_UNPROTECT
122 
123         return(FX_MEDIA_INVALID);
124     }
125 
126     clusters = (FX_FAULT_TOLERANT_MAXIMUM_LOG_FILE_SIZE + bytes_per_cluster - 1) / bytes_per_cluster;
127     media_ptr -> fx_media_fault_tolerant_clusters = clusters;
128 
129     /* Check buffer size requirement. */
130     if (memory_size < bytes_per_sector)
131     {
132 
133         /* Release media protection.  */
134         FX_UNPROTECT
135 
136         return(FX_NOT_ENOUGH_MEMORY);
137     }
138 
139 
140     if (media_ptr -> fx_media_FAT32_additional_info_sector)
141     {
142 
143         /* A 32-bit FAT is present. Read directly into the logical sector
144            cache memory to optimize I/O on larger devices. Since we are looking for
145            values of zero, endian issues are not important.  */
146         /* Force update the available cluster. */
147 
148 #ifndef FX_DISABLE_CACHE
149         /* Invalidate the current logical sector cache.  */
150         _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_TRUE);
151 
152         /* Reset the memory pointer.  */
153         media_ptr -> fx_media_memory_buffer = media_ptr -> fx_media_sector_cache[0].fx_cached_sector_memory_buffer;
154 #endif /* FX_DISABLE_CACHE */
155 
156         /* Reset the available cluster. */
157         media_ptr -> fx_media_available_clusters = 0;
158 
159         /* Loop through all FAT sectors in the primary FAT.  The first two entries are
160            examined in this loop, but they are always unavailable.  */
161         cluster_number =  0;
162 #ifndef FX_DISABLE_CACHE
163         for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i = i + media_ptr -> fx_media_sector_cache_size)
164         {
165 
166             /* Calculate the starting next FAT sector.  */
167             FAT_sector =  media_ptr -> fx_media_reserved_sectors + i;
168 
169             /* Calculate how many sectors to read.  */
170             FAT_read_sectors =  media_ptr -> fx_media_sectors_per_FAT - i;
171 
172             /* Determine if there is not enough memory to read the remaining FAT sectors.  */
173             if (FAT_read_sectors > media_ptr -> fx_media_sector_cache_size)
174             {
175                 FAT_read_sectors =  media_ptr -> fx_media_sector_cache_size;
176             }
177 #else
178         /* Reset the buffer sector.  */
179         media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1;
180         for (i = 0; i < media_ptr -> fx_media_sectors_per_FAT; i++)
181         {
182 
183             /* Calculate the starting next FAT sector.  */
184             FAT_sector =  media_ptr -> fx_media_reserved_sectors + i;
185 
186             /* Calculate how many sectors to read.  */
187             FAT_read_sectors =  1;
188 #endif /* FX_DISABLE_CACHE */
189 
190             /* Read the FAT sectors directly from the driver.  */
191             media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
192             media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
193             media_ptr -> fx_media_driver_buffer =           media_ptr -> fx_media_memory_buffer;
194             media_ptr -> fx_media_driver_logical_sector =   FAT_sector;
195             media_ptr -> fx_media_driver_sectors =          FAT_read_sectors;
196             media_ptr -> fx_media_driver_sector_type =      FX_FAT_SECTOR;
197 
198             /* If trace is enabled, insert this event into the trace buffer.  */
199             FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, FAT_sector, FAT_read_sectors, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
200 
201             /* Invoke the driver to read the FAT sectors.  */
202             (media_ptr -> fx_media_driver_entry) (media_ptr);
203 
204             /* Determine if the read was successful.  */
205             if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
206             {
207 
208                 /* Release media protection.  */
209                 FX_UNPROTECT
210 
211                 return(FX_FAT_READ_ERROR);
212             }
213 
214             /* Calculate the number of bytes in the buffer.  */
215             bytes_in_buffer =  (media_ptr -> fx_media_bytes_per_sector * FAT_read_sectors);
216 
217             /* Walk through the sector cache memory to search for available clusters and the first
218                available if not already found.  */
219             for (j = 0; j < bytes_in_buffer;)
220             {
221 
222                 /* Pickup 32-bit FAT entry.  */
223                 FAT_entry =  *((ULONG *)&(media_ptr -> fx_media_memory_buffer[j]));
224 
225                 /* Advance to next FAT entry.  */
226                 j = j + 4;
227 
228                 /* Determine if the FAT entry is free.  */
229                 if (FAT_entry == FX_FREE_CLUSTER)
230                 {
231 
232                     /* Entry is free, increment available clusters.  */
233                     media_ptr -> fx_media_available_clusters++;
234 
235                     /* Determine if the starting free cluster has been found yet.  */
236                     if (media_ptr -> fx_media_cluster_search_start == 0)
237                     {
238 
239                         /* Remember the first free cluster to start further searches from.  */
240                         media_ptr -> fx_media_cluster_search_start =  cluster_number;
241                     }
242                 }
243 
244                 /* Increment the cluster number.  */
245                 cluster_number++;
246 
247                 /* Determine if we have reviewed all FAT entries.  */
248                 if (cluster_number >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START))
249                 {
250 
251                     /* Yes, we have looked at all the FAT entries.  */
252 
253                     /* Ensure that the outer loop terminates as well.  */
254                     i = media_ptr -> fx_media_sectors_per_FAT;
255                     break;
256                 }
257             }
258         }
259     }
260 
261     /* Store memory buffer and size. */
262     media_ptr -> fx_media_fault_tolerant_memory_buffer = (UCHAR *)memory_buffer;
263     if (memory_size > (clusters * bytes_per_cluster))
264     {
265         media_ptr -> fx_media_fault_tolerant_memory_buffer_size = clusters * bytes_per_cluster;
266     }
267     else
268     {
269         media_ptr -> fx_media_fault_tolerant_memory_buffer_size = memory_size / bytes_per_sector * bytes_per_sector;
270     }
271 
272     /* Read the boot sector from the device.  */
273     media_ptr -> fx_media_driver_request =          FX_DRIVER_BOOT_READ;
274     media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
275     media_ptr -> fx_media_driver_buffer =           (UCHAR *)memory_buffer;
276     media_ptr -> fx_media_driver_sectors =          1;
277     media_ptr -> fx_media_driver_sector_type =      FX_BOOT_SECTOR;
278 
279     /* If trace is enabled, insert this event into the trace buffer.  */
280     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_BOOT_READ, media_ptr, memory_buffer, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
281 
282     /* Invoke the driver to read the boot sector.  */
283     (media_ptr -> fx_media_driver_entry) (media_ptr);
284 
285     /* Determine if the boot sector was read correctly. */
286     if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
287     {
288 
289         /* Release media protection.  */
290         FX_UNPROTECT
291 
292         /* Return the boot sector error status.  */
293         return(FX_BOOT_ERROR);
294     }
295 
296     /* Check whether the boot index is used. */
297     start_cluster = _fx_utility_32_unsigned_read((UCHAR *)memory_buffer + FX_FAULT_TOLERANT_BOOT_INDEX);
298     if (start_cluster != 0)
299     {
300 
301         /* The location of the fault tolerant log file is found.  Need to verify the integrity of the log file. */
302         if ((start_cluster >= FX_FAT_ENTRY_START) && (start_cluster < media_ptr -> fx_media_fat_reserved))
303         {
304 
305             /* Check whether this cluster is used. */
306             for (i = 0; i < clusters; i++)
307             {
308 
309                 /* Read FAT entry.  */
310                 status =  _fx_utility_FAT_entry_read(media_ptr, start_cluster + i, &FAT_value);
311 
312                 /* Check for a bad status.  */
313                 if (status != FX_SUCCESS)
314                 {
315 
316                     /* Release media protection.  */
317                     FX_UNPROTECT
318 
319                     /* Return the bad status.  */
320                     return(status);
321                 }
322 
323                 if (i < clusters - 1)
324                 {
325                     if (FAT_value != (start_cluster + i + 1))
326                     {
327 
328                         /* Mark invalid. */
329                         start_cluster = 0;
330                         break;
331                     }
332                 }
333                 else if (FAT_value != media_ptr -> fx_media_fat_last)
334                 {
335 
336                     /* Mark invalid. */
337                     start_cluster = 0;
338                     break;
339                 }
340             }
341 
342             /* Is this FAT entry occupied by log file? */
343             if (start_cluster)
344             {
345 
346                 /* Set the start cluster. */
347                 media_ptr -> fx_media_fault_tolerant_start_cluster = start_cluster;
348 
349                 /* Read log file from file system to memory. */
350                 status = _fx_fault_tolerant_read_log_file(media_ptr);
351 
352                 /* Check for good completion status.  */
353                 if (status !=  FX_SUCCESS)
354                 {
355 
356                     /* Release media protection.  */
357                     FX_UNPROTECT
358 
359                     /* Return the error status.  */
360                     return(status);
361                 }
362 
363                 /* Set log header and FAT chain pointer. */
364                 log_header = (FX_FAULT_TOLERANT_LOG_HEADER *)media_ptr -> fx_media_fault_tolerant_memory_buffer;
365                 FAT_chain = (FX_FAULT_TOLERANT_FAT_CHAIN *)(media_ptr -> fx_media_fault_tolerant_memory_buffer +
366                                                             FX_FAULT_TOLERANT_FAT_CHAIN_OFFSET);
367 
368                 /* Verify ID field. */
369                 if (_fx_utility_32_unsigned_read((UCHAR *)&log_header -> fx_fault_tolerant_log_header_id) == FX_FAULT_TOLERANT_ID)
370                 {
371 
372                     /* Calculate checksum of log header. */
373                     checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)log_header,
374                                                                      FX_FAULT_TOLERANT_LOG_HEADER_SIZE);
375 
376                     if (checksum == 0)
377                     {
378 
379                         /* Fault tolerant log file is valid. */
380                         /* Initialize file size. */
381                         total_size = _fx_utility_16_unsigned_read((UCHAR *)&log_header -> fx_fault_tolerant_log_header_total_size);
382                         media_ptr -> fx_media_fault_tolerant_file_size = total_size;
383 
384 
385                         /* Verify the checksum of the FAT chain. */
386                         checksum = _fx_fault_tolerant_calculate_checksum((UCHAR *)FAT_chain,
387                                                                          FX_FAULT_TOLERANT_FAT_CHAIN_SIZE);
388 
389                         if (checksum == 0)
390                         {
391 
392                             /* Checksum of FAT chain is correct. */
393 
394                             if (total_size > (FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET + FX_FAULT_TOLERANT_LOG_HEADER_SIZE))
395                             {
396 
397                                 /* Log content is present. */
398                                 /* Now verify the checksum of log content. */
399                                 checksum = _fx_fault_tolerant_calculate_checksum((media_ptr -> fx_media_fault_tolerant_memory_buffer +
400                                                                                   FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET),
401                                                                                  (total_size - FX_FAULT_TOLERANT_LOG_CONTENT_OFFSET));
402                                 if (checksum == 0)
403                                 {
404 
405                                     /* Checksum of log content is correct. */
406 
407                                     /* Extended port-specific processing macro, which is by default defined to white space.  */
408                                     FX_FAULT_TOLERANT_ENABLE_EXTENSION
409 
410                                     /* This is the situation where the log file contains log entries.  This is an indication
411                                        that previous write operation did not complete successfully.  Need to apply the log entries
412                                        to recover the previous write operation, effectively to finish up the previous write operation. */
413                                     status = _fx_fault_tolerant_apply_logs(media_ptr);
414                                 }
415                             }
416                             else
417                             {
418 
419                                 /* Extended port-specific processing macro, which is by default defined to white space.  */
420                                 FX_FAULT_TOLERANT_ENABLE_EXTENSION
421 
422                                 /* The log file does not contain log content but the FAT chain operation information is present.
423                                    This is the situation where the FAT chain has been modified but the rest of the content of these
424                                    clusters are not updated yet.  In this situation, the previous FAT chain operation needs to be
425                                    reverted to restore the file system back to its state prior to the write operation. */
426                                 status = _fx_fault_tolerant_recover(media_ptr);
427                             }
428 
429                             if (status !=  FX_SUCCESS)
430                             {
431 
432                                 /* Release media protection.  */
433                                 FX_UNPROTECT
434 
435                                 /* Return the error status.  */
436                                 return(status);
437                             }
438                         }
439                     }
440                 }
441             }
442         }
443         else
444         {
445 
446             /* Not a valid cluster number. Set the flag to create a new log file. */
447             start_cluster = 0;
448         }
449     }
450 
451     /* Check whether or not to create a log file. */
452     if (start_cluster == 0)
453     {
454 
455         /* Create log file. */
456         status = _fx_fault_tolerant_create_log_file(media_ptr);
457 
458         if (status !=  FX_SUCCESS)
459         {
460 
461             /* Release media protection.  */
462             FX_UNPROTECT
463 
464             /* Return the error status.  */
465             return(status);
466         }
467     }
468 
469     /* Reset log file. */
470     status = _fx_fault_tolerant_reset_log_file(media_ptr);
471 
472     if (status !=  FX_SUCCESS)
473     {
474 
475         /* Release media protection.  */
476         FX_UNPROTECT
477 
478         /* Return the error status.  */
479         return(status);
480     }
481 
482     /* Mark fault tolerant feature is enabled. */
483     media_ptr -> fx_media_fault_tolerant_enabled = FX_TRUE;
484 
485     /* Reset the transaction count. */
486     media_ptr -> fx_media_fault_tolerant_transaction_count = 0;
487 
488     /* Initialize the sector number of cached FAT entries. */
489     media_ptr -> fx_media_fault_tolerant_cached_FAT_sector = 0;
490 
491     /* Release media protection.  */
492     FX_UNPROTECT
493 
494     /* Return the error status.  */
495     return(FX_SUCCESS);
496 }
497 
498 #endif /* FX_ENABLE_FAULT_TOLERANT */
499 
500