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 /**   Utility                                                             */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define FX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "fx_api.h"
28 #include "fx_system.h"
29 #include "fx_utility.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _fx_utility_FAT_flush                               PORTABLE C      */
37 /*                                                           6.1.2        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    William E. Lamie, Microsoft Corporation                             */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function flushes the contents of the FAT cache to the media.   */
45 /*    12-bit, 16-bit and 32-bit FAT writing is supported.                 */
46 /*                                                                        */
47 /*  INPUT                                                                 */
48 /*                                                                        */
49 /*    media_ptr                             Media control block pointer   */
50 /*                                                                        */
51 /*  OUTPUT                                                                */
52 /*                                                                        */
53 /*    return status                                                       */
54 /*                                                                        */
55 /*  CALLS                                                                 */
56 /*                                                                        */
57 /*    _fx_utility_16_unsigned_write         Write a UINT into buffer      */
58 /*    _fx_utility_32_unsigned_read          Read a ULONG from buffer      */
59 /*    _fx_utility_32_unsigned_write         Write a ULONG into buffer     */
60 /*    _fx_utility_logical_sector_read       Read FAT sector into memory   */
61 /*    _fx_utility_logical_sector_write      Write FAT sector back to disk */
62 /*                                                                        */
63 /*  CALLED BY                                                             */
64 /*                                                                        */
65 /*    FileX System Functions                                              */
66 /*                                                                        */
67 /*  RELEASE HISTORY                                                       */
68 /*                                                                        */
69 /*    DATE              NAME                      DESCRIPTION             */
70 /*                                                                        */
71 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
72 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
73 /*                                            resulting in version 6.1    */
74 /*  11-09-2020     William E. Lamie         Modified comment(s),          */
75 /*                                            updated logic for           */
76 /*                                            FAT secondary update map,   */
77 /*                                            resulting in version 6.1.2  */
78 /*                                                                        */
79 /**************************************************************************/
_fx_utility_FAT_flush(FX_MEDIA * media_ptr)80 UINT  _fx_utility_FAT_flush(FX_MEDIA *media_ptr)
81 {
82 
83 ULONG  FAT_sector;
84 ULONG  byte_offset;
85 UCHAR *FAT_ptr;
86 UINT   temp, i;
87 UINT   status, index, ind;
88 ULONG  cluster, next_cluster;
89 UCHAR  sectors_per_bit;
90 INT    multi_sector_entry;
91 ULONG  sector;
92 
93 #ifndef FX_MEDIA_STATISTICS_DISABLE
94     /* Increment the number of cache flush requests.  */
95     media_ptr -> fx_media_fat_cache_flushes++;
96 #endif
97 
98     /* Loop through the media's FAT cache and flush out dirty entries.  */
99     for (index = 0; index < FX_MAX_FAT_CACHE; index++)
100     {
101 
102         /* Determine if the entry is dirty.  */
103         if ((media_ptr -> fx_media_fat_cache[index].fx_fat_cache_entry_dirty) == 0)
104         {
105 
106             /* No, just advance to the next entry.  */
107             continue;
108         }
109 
110         /* Otherwise, the entry is indeed dirty and must be flushed out.  Process
111            relative to the type of FAT that is being used.  */
112 
113         /* Pickup the contents of the FAT cache entry.  */
114         cluster =       media_ptr -> fx_media_fat_cache[index].fx_fat_cache_entry_cluster;
115 
116         /* Determine which type of FAT is present.  */
117         if (media_ptr -> fx_media_12_bit_FAT)
118         {
119 
120             /* Calculate the byte offset to the cluster entry.  */
121             byte_offset =  (((ULONG)cluster << 1) + cluster) >> 1;
122 
123             /* Calculate the FAT sector the requested FAT entry resides in.  */
124             FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
125                 (ULONG)media_ptr -> fx_media_reserved_sectors;
126 
127             /* Initialize as not written.  */
128             multi_sector_entry = -1;
129 
130             for (;;)
131             {
132 
133                 /* Pickup the FAT sector.  */
134                 status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
135                                                           media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
136 
137                 /* Determine if an error occurred.  */
138                 if (status != FX_SUCCESS)
139                 {
140 
141                     /* Return the error status.  */
142                     return(status);
143                 }
144 
145                 /* Determine if a mulit-sector FAT update is present.  */
146                 if (multi_sector_entry != -1)
147                 {
148 
149                     /* Yes, store the remaining portion of the new FAT entry in the
150                        next FAT sector.  */
151 
152                     /* Setup a pointer into the buffer.  */
153                     FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer;
154 
155                     /* Pickup the cluster and next cluster.  */
156                     cluster = (media_ptr -> fx_media_fat_cache[multi_sector_entry].fx_fat_cache_entry_cluster);
157                     next_cluster = media_ptr -> fx_media_fat_cache[multi_sector_entry].fx_fat_cache_entry_value;
158 
159                     /* Determine if the cluster entry is odd or even.  */
160                     if (cluster & 1)
161                     {
162 
163                         /* Store the upper 8 bits of the FAT entry.  */
164                         *FAT_ptr =  (UCHAR)((next_cluster >> 4) & 0xFF);
165                     }
166                     else
167                     {
168 
169                         /* Store the upper 4 bits of the FAT entry.  */
170                         temp =  ((UINT)*FAT_ptr) & 0xF0;
171                         *FAT_ptr =  (UCHAR)(temp | ((next_cluster >> 8) & 0xF));
172                     }
173 
174                     /* Clear the multi-sector flag.  */
175                     multi_sector_entry = -1;
176                 }
177 
178                 /* Loop through the remainder of the cache to check for multiple entries
179                    within the same FAT sector being written out.  */
180                 for (i = index; i < FX_MAX_FAT_CACHE; i++)
181                 {
182 
183                     /* Is the cache entry dirty?  */
184                     if ((media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty) == 0)
185                     {
186 
187                         /* Not dirty, does not need to be flushed.  */
188                         continue;
189                     }
190 
191                     /* Isolate the cluster.  */
192                     cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster);
193 
194                     /* Calculate the byte offset to the cluster entry.  */
195                     byte_offset =  (((ULONG)cluster << 1) + cluster) >> 1;
196 
197                     /* Pickup the sector.  */
198                     sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
199                         (ULONG)media_ptr -> fx_media_reserved_sectors;
200 
201                     /* Is it the current FAT sector?  */
202                     if (sector != FAT_sector)
203                     {
204 
205                         /* Different FAT sector - not in this pass of the loop.  */
206                         continue;
207                     }
208 
209                     /* Pickup new value for this FAT entry.  */
210                     next_cluster =  media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value;
211 
212                     /* Now calculate the byte offset into this FAT sector.  */
213                     byte_offset =  byte_offset -
214                         ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
215                          media_ptr -> fx_media_bytes_per_sector);
216 
217                     /* Determine if we are now past the end of the FAT buffer in memory.  */
218                     if (byte_offset == (ULONG)(media_ptr -> fx_media_bytes_per_sector - 1))
219                     {
220 
221                         /* Yes, we need to read the next sector */
222                         multi_sector_entry = (INT)i;
223                     }
224 
225                     /* Setup a pointer into the buffer.  */
226                     FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
227 
228                     /* Clear the dirty flag.  */
229                     media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0;
230 
231                     /* Determine if the cluster entry is odd or even.  */
232                     if (cluster & 1)
233                     {
234 
235                         /* Odd cluster number.  */
236 
237                         /* Pickup the upper nibble of the FAT entry.  */
238 
239                         /* First, set the lower nibble of the FAT entry.  */
240                         temp =      (((UINT)*FAT_ptr) & 0x0F);
241                         *FAT_ptr =  (UCHAR)(temp | ((next_cluster << 4) & 0xF0));
242 
243                         /* Determine if this is a mulit-sector entry.  */
244                         if ((multi_sector_entry) == (INT)i)
245                         {
246 
247                             /* Yes, requires multiple sector - will write rest of the part later.  */
248                             continue;
249                         }
250 
251                         /* Move to the next byte of the FAT entry.  */
252                         FAT_ptr++;
253 
254                         /* Store the upper 8 bits of the FAT entry.  */
255                         *FAT_ptr =  (UCHAR)((next_cluster >> 4) & 0xFF);
256                     }
257                     else
258                     {
259 
260                         /* Even cluster number.  */
261 
262                         /* Store the lower byte of the FAT entry.  */
263                         *FAT_ptr =  (UCHAR)(next_cluster & 0xFF);
264 
265                         /* Determine if this is a mulit-sector entry.  */
266                         if ((multi_sector_entry) == (INT)i)
267                         {
268 
269                             /* Yes, requires multiple sector - will write rest of the part later.  */
270                             continue;
271                         }
272 
273                         /* Move to the next nibble of the FAT entry.  */
274                         FAT_ptr++;
275 
276                         /* Store the upper 4 bits of the FAT entry.  */
277                         temp =  ((UINT)*FAT_ptr) & 0xF0;
278                         *FAT_ptr =  (UCHAR)(temp | ((next_cluster >> 8) & 0xF));
279                     }
280                 }
281 
282                 /* First, write out the current sector. */
283                 status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector,
284                                                            media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
285                 /* Determine if an error occurred.  */
286                 if (status != FX_SUCCESS)
287                 {
288 
289                     /* Return the error status.  */
290                     return(status);
291                 }
292 
293                 /* Mark the FAT sector update bit map to indicate this sector has been written.  */
294                 if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0)
295                 {
296                     sectors_per_bit =  (UCHAR)((UINT)media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3));
297                 }
298                 else
299                 {
300                     sectors_per_bit =  (UCHAR)((UINT)media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3) + 1);
301                 }
302 
303                 /* Check for invalid value.  */
304                 if (sectors_per_bit == 0)
305                 {
306 
307                     /* Invalid media, return error.  */
308                     return(FX_MEDIA_INVALID);
309                 }
310 
311                 ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3;
312                 media_ptr -> fx_media_fat_secondary_update_map[ind] =
313                     (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind]
314                     | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7)));
315 
316                 /* Determine if the multi-sector flag is set.  */
317                 if (multi_sector_entry != -1)
318                 {
319 
320                     /* Yes, position to the next sector and read it in.  */
321                     FAT_sector++;
322                 }
323                 else
324                 {
325 
326                     /* No, we are finished with this loop.   */
327                     break;
328                 }
329             }
330         }
331         else if (!media_ptr -> fx_media_32_bit_FAT)
332         {
333 
334             /* 16-bit FAT is present.  */
335 
336             /* Calculate the byte offset to the cluster entry.  */
337             byte_offset =  (((ULONG)cluster) << 1);
338 
339             /* Calculate the FAT sector the requested FAT entry resides in.  */
340             FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
341                 (ULONG)media_ptr -> fx_media_reserved_sectors;
342 
343             /* Read the FAT sector.  */
344             status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
345                                                       media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
346 
347             /* Determine if an error occurred.  */
348             if (status != FX_SUCCESS)
349             {
350 
351                 /* Return the error status.  */
352                 return(status);
353             }
354 
355             /* Loop through the remainder of the cache to check for multiple entries
356                within the same FAT sector being written out.  */
357             for (i = index; i < FX_MAX_FAT_CACHE; i++)
358             {
359 
360                 /* Determine if the entry is dirty.  */
361                 if (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty == 0)
362                 {
363 
364                     /* Not dirty, does not need to be flushed.  */
365                     continue;
366                 }
367 
368                 /* Isolate the cluster.  */
369                 cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster);
370 
371                 /* Calculate the byte offset to the cluster entry.  */
372                 byte_offset =  (((ULONG)cluster) * 2);
373 
374                 /* Pickup the sector.  */
375                 sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
376                     (ULONG)media_ptr -> fx_media_reserved_sectors;
377 
378                 /* Is it the current FAT sector?  */
379                 if (sector != FAT_sector)
380                 {
381 
382                     /* Different FAT sector - not in this pass of the loop.  */
383                     continue;
384                 }
385 
386                 /* Now calculate the byte offset into this FAT sector.  */
387                 byte_offset =  byte_offset -
388                     ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
389                      media_ptr -> fx_media_bytes_per_sector);
390 
391                 /* Setup a pointer into the buffer.  */
392                 FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
393 
394                 /* Pickup new value for this FAT entry.  */
395                 next_cluster =  media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value;
396 
397                 /* Store the FAT entry.  */
398                 _fx_utility_16_unsigned_write(FAT_ptr, (UINT)next_cluster);
399 
400                 /* Clear the dirty flag.  */
401                 media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0;
402             }
403 
404             /* Write the last written FAT sector out.  */
405             status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector,
406                                                        media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
407 
408             /* Determine if an error occurred.  */
409             if (status != FX_SUCCESS)
410             {
411                 /* Return the error status.  */
412                 return(status);
413             }
414 
415             /* Mark the FAT sector update bit map to indicate this sector has been
416                written.  */
417             if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0)
418             {
419                 sectors_per_bit =  (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3));
420             }
421             else
422             {
423                 sectors_per_bit =  (UCHAR)((media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)) + 1);
424             }
425             ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3;
426             media_ptr -> fx_media_fat_secondary_update_map[ind] =
427                 (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind]
428                 | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7)));
429         }
430         else
431         {
432 
433             /* 32-bit FAT is present.  */
434 
435             /* Calculate the byte offset to the cluster entry.  */
436             byte_offset =  (((ULONG)cluster) * 4);
437 
438             /* Calculate the FAT sector the requested FAT entry resides in.  */
439             FAT_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
440                 (ULONG)media_ptr -> fx_media_reserved_sectors;
441 
442             /* Read the FAT sector.  */
443             status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) FAT_sector,
444                                                       media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
445 
446             /* Determine if an error occurred.  */
447             if (status != FX_SUCCESS)
448             {
449 
450                 /* Return the error status.  */
451                 return(status);
452             }
453 
454             /* Loop through the remainder of the cache to check for multiple entries
455                within the same FAT sector being written out.  */
456             for (i = index; i < FX_MAX_FAT_CACHE; i++)
457             {
458 
459                 /* Determine if the entry is dirty.  */
460                 if (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty == 0)
461                 {
462 
463                     /* Not dirty, does not need to be flushed.  */
464                     continue;
465                 }
466 
467                 /* Isolate the cluster.  */
468                 cluster = (media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_cluster);
469 
470                 /* Calculate the byte offset to the cluster entry.  */
471                 byte_offset =  (((ULONG)cluster) * 4);
472 
473                 /* Pickup the sector.  */
474                 sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
475                     (ULONG)media_ptr -> fx_media_reserved_sectors;
476 
477                 /* Is it the current FAT sector?  */
478                 if (sector != FAT_sector)
479                 {
480 
481                     /* Different FAT sector - not in this pass of the loop.  */
482                     continue;
483                 }
484 
485                 /* Now calculate the byte offset into this FAT sector.  */
486                 byte_offset =  byte_offset -
487                     ((FAT_sector - (ULONG)media_ptr -> fx_media_reserved_sectors) *
488                      media_ptr -> fx_media_bytes_per_sector);
489 
490                 /* Setup a pointer into the buffer.  */
491                 FAT_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
492 
493                 /* Pickup new value for this FAT entry.  */
494                 next_cluster =  media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_value;
495 
496                 /* Store the FAT entry.  */
497                 _fx_utility_32_unsigned_write(FAT_ptr, next_cluster);
498 
499                 /* Clear the dirty flag.  */
500                 media_ptr -> fx_media_fat_cache[i].fx_fat_cache_entry_dirty = 0;
501             }
502 
503             /* Write the last written FAT sector out.  */
504             status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) FAT_sector,
505                                                        media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_FAT_SECTOR);
506 
507             /* Determine if an error occurred.  */
508             if (status != FX_SUCCESS)
509             {
510 
511                 /* Return the error status.  */
512                 return(status);
513             }
514 
515 
516             /* Mark the FAT sector update bit map to indicate this sector has been
517                written.  */
518             if (media_ptr -> fx_media_sectors_per_FAT % (FX_FAT_MAP_SIZE << 3) == 0)
519             {
520                 sectors_per_bit =  (UCHAR)(media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3));
521             }
522             else
523             {
524                 sectors_per_bit =  (UCHAR)((media_ptr -> fx_media_sectors_per_FAT / (FX_FAT_MAP_SIZE << 3)) + 1);
525             }
526             ind = ((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) >> 3;
527             media_ptr -> fx_media_fat_secondary_update_map[ind] =
528                 (UCHAR)((INT)media_ptr -> fx_media_fat_secondary_update_map[ind]
529                 | (1 <<(((FAT_sector - media_ptr -> fx_media_reserved_sectors) / sectors_per_bit) & 7)));
530         }
531     }
532 
533 #ifdef FX_ENABLE_FAULT_TOLERANT
534 
535     /* While fault_tolerant is enabled, this function will be called before the return of all APIs. */
536     if (media_ptr -> fx_media_fault_tolerant_enabled)
537     {
538 
539         /* Delete the record of FAT sector since all FAT entries are flushed. */
540         media_ptr -> fx_media_fault_tolerant_cached_FAT_sector = 0;
541     }
542 #endif /* FX_ENABLE_FAULT_TOLERANT */
543 
544     /* Return successful status.  */
545     return(FX_SUCCESS);
546 }
547 
548