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 /**   File                                                                */
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_file.h"
30 #include "fx_utility.h"
31 #include "fx_directory.h"
32 #ifdef FX_ENABLE_FAULT_TOLERANT
33 #include "fx_fault_tolerant.h"
34 #endif /* FX_ENABLE_FAULT_TOLERANT */
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _fx_file_extended_best_effort_allocate              PORTABLE C      */
42 /*                                                           6.1          */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    William E. Lamie, Microsoft Corporation                             */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function attempts to allocate the number of consecutive        */
50 /*    clusters required to satisfy the user's request.  If there are not  */
51 /*    enough clusters, the largest set of clusters are allocated and      */
52 /*    linked to the file.  If there are no free clusters, an error        */
53 /*    code is returned to the caller.                                     */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    file_ptr                              File control block pointer    */
58 /*    size                                  Number of bytes to allocate   */
59 /*    actual_size_allocated                 Number of bytes allocated     */
60 /*                                                                        */
61 /*  OUTPUT                                                                */
62 /*                                                                        */
63 /*    return status                                                       */
64 /*                                                                        */
65 /*  CALLS                                                                 */
66 /*                                                                        */
67 /*    _fx_directory_entry_write             Update directory entry        */
68 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
69 /*    _fx_utility_FAT_entry_write           Write a FAT entry             */
70 /*    _fx_utility_FAT_flush                 Flush written FAT entries     */
71 /*    _fx_utility_logical_sector_flush      Flush the written log sector  */
72 /*    _fx_fault_tolerant_transaction_start  Start fault tolerant          */
73 /*                                            transaction                 */
74 /*    _fx_fault_tolerant_transaction_end    End fault tolerant transaction*/
75 /*    _fx_fault_tolerant_recover            Recover FAT chain             */
76 /*    _fx_fault_tolerant_reset_log_file     Reset the log file            */
77 /*    _fx_fault_tolerant_set_FAT_chain      Set data of FAT chain         */
78 /*                                                                        */
79 /*  CALLED BY                                                             */
80 /*                                                                        */
81 /*    Application Code                                                    */
82 /*                                                                        */
83 /*  RELEASE HISTORY                                                       */
84 /*                                                                        */
85 /*    DATE              NAME                      DESCRIPTION             */
86 /*                                                                        */
87 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
88 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
89 /*                                            resulting in version 6.1    */
90 /*                                                                        */
91 /**************************************************************************/
_fx_file_extended_best_effort_allocate(FX_FILE * file_ptr,ULONG64 size,ULONG64 * actual_size_allocated)92 UINT  _fx_file_extended_best_effort_allocate(FX_FILE *file_ptr, ULONG64 size, ULONG64 *actual_size_allocated)
93 {
94 
95 UINT                   status;
96 ULONG                  i;
97 UINT                   found;
98 ULONG                  bytes_per_cluster;
99 ULONG                  FAT_index, start_FAT_index;
100 ULONG                  FAT_value;
101 ULONG                  clusters, maximum_clusters;
102 FX_MEDIA              *media_ptr;
103 
104 
105 #ifdef TX_ENABLE_EVENT_TRACE
106 TX_TRACE_BUFFER_ENTRY *trace_event;
107 ULONG                  trace_timestamp;
108 #endif
109 
110 
111     /* First, determine if the file is still open.  */
112     if (file_ptr -> fx_file_id != FX_FILE_ID)
113     {
114 
115         /* Return the file not open error status.  */
116         return(FX_NOT_OPEN);
117     }
118 
119 #ifndef FX_MEDIA_STATISTICS_DISABLE
120     /* Setup pointer to media structure.  */
121     media_ptr =  file_ptr -> fx_file_media_ptr;
122 
123     /* Increment the number of times this service has been called.  */
124     media_ptr -> fx_media_file_best_effort_allocates++;
125 #endif
126 
127     /* Make sure this file is open for writing.  */
128     if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE)
129     {
130 
131         /* Return the access error exception - a write was attempted from
132            a file opened for reading!  */
133         return(FX_ACCESS_ERROR);
134     }
135 
136     /* Determine if the requested allocation is for zero bytes.  */
137     if (size == 0)
138     {
139 
140         /* Return a size allocated of zero.  */
141         *actual_size_allocated =  0;
142 
143         /* Return a successful completion - nothing needs to be done.  */
144         return(FX_SUCCESS);
145     }
146 
147     /* Setup pointer to associated media control block.  */
148     media_ptr =  file_ptr -> fx_file_media_ptr;
149 
150     /* If trace is enabled, insert this event into the trace buffer.  */
151     FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_BEST_EFFORT_ALLOCATE, file_ptr, size, 0, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp)
152 
153     /* Protect against other threads accessing the media.  */
154     FX_PROTECT
155 
156 #ifdef FX_ENABLE_FAULT_TOLERANT
157     /* Start transaction. */
158     _fx_fault_tolerant_transaction_start(media_ptr);
159 #endif /* FX_ENABLE_FAULT_TOLERANT */
160 
161     /* Check for write protect at the media level (set by driver).  */
162     if (media_ptr -> fx_media_driver_write_protect)
163     {
164 
165 #ifdef FX_ENABLE_FAULT_TOLERANT
166         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
167 #endif /* FX_ENABLE_FAULT_TOLERANT */
168 
169         /* Release media protection.  */
170         FX_UNPROTECT
171 
172         /* Return write protect error.  */
173         return(FX_WRITE_PROTECT);
174     }
175 
176     /* Calculate the number of bytes per cluster.  */
177     bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
178         ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
179 
180     /* Check for invalid value.  */
181     if (bytes_per_cluster == 0)
182     {
183 
184 #ifdef FX_ENABLE_FAULT_TOLERANT
185         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
186 #endif /* FX_ENABLE_FAULT_TOLERANT */
187 
188         /* Release media protection.  */
189         FX_UNPROTECT
190 
191         /* Invalid media, return error.  */
192         return(FX_MEDIA_INVALID);
193     }
194 
195     /* Calculate the number of consecutive clusters needed to satisfy this
196        request.  */
197     clusters =  (ULONG)(((size + bytes_per_cluster - 1) / bytes_per_cluster));
198 
199     /* Determine if cluster count is 0.  */
200     if (clusters == 0)
201     {
202 
203 #ifdef FX_ENABLE_FAULT_TOLERANT
204         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
205 #endif /* FX_ENABLE_FAULT_TOLERANT */
206 
207         /* Release media protection.  */
208         FX_UNPROTECT
209 
210         /* Size overflow when rounding to the next cluster, return an error status.  */
211         return(FX_NO_MORE_SPACE);
212     }
213 
214     /* Determine if there are no available clusters on the media.  */
215     if (!media_ptr -> fx_media_available_clusters)
216     {
217 
218 #ifdef FX_ENABLE_FAULT_TOLERANT
219         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
220 #endif /* FX_ENABLE_FAULT_TOLERANT */
221 
222         /* Release media protection.  */
223         FX_UNPROTECT
224 
225         /* Return a size allocated of zero, since no clusters were available.  */
226         *actual_size_allocated =  0;
227 
228         /* Not enough clusters, return an error status.  */
229         return(FX_NO_MORE_SPACE);
230     }
231 
232     /* Determine if the requested file allocation would exceed the physical limit of the file.  */
233     if (((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) < file_ptr -> fx_file_current_available_size) ||
234         ((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) > 0xFFFFFFFFULL))
235     {
236 
237 #ifdef FX_ENABLE_FAULT_TOLERANT
238         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
239 #endif /* FX_ENABLE_FAULT_TOLERANT */
240 
241         /* Release media protection.  */
242         FX_UNPROTECT
243 
244         /* Return the no more space error, since the new file size would be larger than
245            the 32-bit field to represent it in the file's directory entry.  */
246         return(FX_NO_MORE_SPACE);
247     }
248 
249     /* Now we need to find the consecutive clusters.  */
250     found =             FX_FALSE;
251     FAT_index =         FX_FAT_ENTRY_START;
252     maximum_clusters =  0;
253     start_FAT_index =   FAT_index;
254 
255     while (FAT_index < (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START))
256     {
257 
258         /* Determine if enough consecutive FAT entries are available.  */
259         i =  0;
260 
261 
262         do
263         {
264 
265             /* Read a FAT entry.  */
266             status =  _fx_utility_FAT_entry_read(media_ptr, (FAT_index + i), &FAT_value);
267 
268             /* Check for a successful status.  */
269             if (status != FX_SUCCESS)
270             {
271 
272 #ifdef FX_ENABLE_FAULT_TOLERANT
273                 FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
274 #endif /* FX_ENABLE_FAULT_TOLERANT */
275 
276                 /* Release media protection.  */
277                 FX_UNPROTECT
278 
279                 /* Return the error status.  */
280                 return(status);
281             }
282 
283             /* Determine if the entry is free.  */
284             if (FAT_value != FX_FREE_CLUSTER)
285             {
286                 break;
287             }
288 
289             /* Otherwise, increment the consecutive FAT indices.  */
290             i++;
291         } while ((i < clusters) && ((FAT_index + i) < media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START));
292 
293         /* Determine if a new maximum number of clusters has been found.  */
294         if (i > maximum_clusters)
295         {
296 
297             /* Yes, remember the maximum number of clusters and the starting
298                cluster.  */
299             maximum_clusters =      i;
300             start_FAT_index =       FAT_index;
301         }
302 
303         /* Determine if we found enough FAT entries.  */
304         if (i >= clusters)
305         {
306 
307             /* Yes, we have found enough FAT entries - set the found
308                flag and get out of this loop.  */
309             found =  FX_TRUE;
310             break;
311         }
312         else
313         {
314 
315             /* Position to the next possibly free FAT entry.  */
316             FAT_index =  FAT_index + i + 1;
317     }
318 }
319 
320     /* Determine if the total request could not be satisfied, but a partial allocation
321        could be satisfied.  */
322     if (maximum_clusters)
323     {
324 
325         /* Yes, there was at least one cluster.  Prepare to return this
326            to the caller.  */
327         FAT_index =  start_FAT_index;
328         clusters =   maximum_clusters;
329         found =      FX_TRUE;
330     }
331 
332     /* Determine if we found enough consecutive clusters to satisfy the
333        request.  */
334     if (found)
335     {
336 
337 #ifdef FX_ENABLE_FAULT_TOLERANT
338         if (media_ptr -> fx_media_fault_tolerant_enabled)
339         {
340 
341             /* Record the FAT chain being applied to the file system. This information aids
342                recovery effort if fault happens. */
343             media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN;
344             _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, file_ptr -> fx_file_last_physical_cluster,
345                                              FAT_index, media_ptr -> fx_media_fat_last, media_ptr -> fx_media_fat_last);
346         }
347 #endif /* FX_ENABLE_FAULT_TOLERANT */
348 
349 
350         /* Update the link pointers in the new clusters.  */
351         for (i = 0; i < (clusters - 1); i++)
352         {
353 
354             /* Update the cluster links.  Since the allocation is
355                sequential, we just have to link each FAT entry to the
356                next one.  */
357             status =  _fx_utility_FAT_entry_write(media_ptr, FAT_index + i, FAT_index + i + 1);
358 
359             /* Check for a bad status.  */
360             if (status != FX_SUCCESS)
361             {
362 
363 #ifdef FX_ENABLE_FAULT_TOLERANT
364                 FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
365 #endif /* FX_ENABLE_FAULT_TOLERANT */
366 
367                 /* Release media protection.  */
368                 FX_UNPROTECT
369 
370                 /* Return the error status.  */
371                 return(status);
372             }
373         }
374 
375 
376         /* Now place an EOF in the last cluster entry.  */
377         status =  _fx_utility_FAT_entry_write(media_ptr, FAT_index + clusters - 1, media_ptr -> fx_media_fat_last);
378 
379         /* Check for a bad status.  */
380         if (status != FX_SUCCESS)
381         {
382 
383 #ifdef FX_ENABLE_FAULT_TOLERANT
384             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
385 #endif /* FX_ENABLE_FAULT_TOLERANT */
386 
387             /* Release media protection.  */
388             FX_UNPROTECT
389 
390             /* Return the error status.  */
391             return(status);
392         }
393 
394 #ifdef FX_FAULT_TOLERANT
395 
396         /* Flush the cached individual FAT entries */
397         _fx_utility_FAT_flush(media_ptr);
398 #endif
399 
400         /* Actually link up the new clusters to the file.  */
401 
402         /* Determine if there are already clusters allocated for this file.  */
403         if (file_ptr -> fx_file_total_clusters)
404         {
405 
406 
407             /* Linkup the last cluster.  */
408             status =  _fx_utility_FAT_entry_write(media_ptr,
409                                                   file_ptr -> fx_file_last_physical_cluster, FAT_index);
410 
411             /* Check for a bad status.  */
412             if (status != FX_SUCCESS)
413             {
414 
415 #ifdef FX_ENABLE_FAULT_TOLERANT
416                 FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
417 #endif /* FX_ENABLE_FAULT_TOLERANT */
418 
419                 /* Release media protection.  */
420                 FX_UNPROTECT
421 
422                 /* Return the error status.  */
423                 return(status);
424             }
425 
426             /* Determine if we are adding a sector after a write filled the previously
427                allocated cluster exactly.  */
428             if ((file_ptr -> fx_file_current_relative_sector >=
429                  (media_ptr -> fx_media_sectors_per_cluster - 1)) &&
430                 (file_ptr -> fx_file_current_logical_offset >=
431                     media_ptr -> fx_media_bytes_per_sector))
432             {
433 
434                 /* Yes, we need to adjust all of the pertinent file parameters for
435                    access into this newly allocated cluster.  */
436                 file_ptr -> fx_file_current_physical_cluster =  FAT_index;
437                 file_ptr -> fx_file_current_relative_cluster++;
438                 file_ptr -> fx_file_current_relative_sector =   0;
439                 file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
440                     (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) *
441                      ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
442                 file_ptr -> fx_file_current_logical_offset =    0;
443             }
444         }
445         else
446         {
447 
448             /* These new clusters are also the first!  Setup the initial
449                file parameters.  */
450             file_ptr -> fx_file_first_physical_cluster =    FAT_index;
451             file_ptr -> fx_file_current_physical_cluster =  file_ptr -> fx_file_first_physical_cluster;
452             file_ptr -> fx_file_current_relative_cluster =  0;
453             file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
454                 (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) *
455                  ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
456             file_ptr -> fx_file_current_logical_offset =    0;
457             file_ptr -> fx_file_current_file_offset =       0;
458 
459             /* Setup the consecutive clusters at the beginning of the file.  */
460             file_ptr -> fx_file_consecutive_cluster =       clusters;
461 
462             /* Update the first cluster in the directory entry.  */
463             file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster =  FAT_index;
464 
465 #ifdef FX_ENABLE_FAULT_TOLERANT
466             if (media_ptr -> fx_media_fault_tolerant_enabled)
467             {
468 
469                 /* Clear undo phase. */
470                 media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff);
471             }
472 #endif /* FX_ENABLE_FAULT_TOLERANT */
473         }
474 
475         /* Remember the last physical cluster.  */
476         file_ptr -> fx_file_last_physical_cluster =     FAT_index + clusters - 1;
477 
478         /* Update the available size.  */
479 
480         /* Normal condition, update the available size.  */
481         file_ptr -> fx_file_current_available_size =
482             file_ptr -> fx_file_current_available_size + bytes_per_cluster * clusters;
483 
484         /* Increment the total clusters for this file.  */
485         file_ptr -> fx_file_total_clusters =
486             file_ptr -> fx_file_total_clusters + clusters;
487 
488         /* Decrease the available clusters on the media.  */
489         media_ptr -> fx_media_available_clusters =
490             media_ptr -> fx_media_available_clusters - clusters;
491 
492         /* Return the actual size allocated.  */
493         *actual_size_allocated =  ((ULONG64)clusters) * bytes_per_cluster;
494 
495 #if defined(FX_UPDATE_FILE_SIZE_ON_ALLOCATE) || defined(FX_ENABLE_FAULT_TOLERANT)
496 
497         /* Set the file size the current size plus what what was added.  */
498         if (size < *actual_size_allocated)
499         {
500             file_ptr -> fx_file_current_file_size +=  size;
501         }
502         else
503         {
504             file_ptr -> fx_file_current_file_size +=  *actual_size_allocated;
505         }
506 
507         /* Copy the new file size into the directory entry.  */
508         file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size =  file_ptr -> fx_file_current_file_size;
509 #endif
510 
511         /* Update the trace event with the bytes allocated.  */
512         FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_BEST_EFFORT_ALLOCATE, 0, 0, *actual_size_allocated, 0);
513 
514         /* Write the directory entry to the media.  */
515         status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry));
516 
517         /* Check for a good status.  */
518         if (status != FX_SUCCESS)
519         {
520 
521 #ifdef FX_ENABLE_FAULT_TOLERANT
522             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
523 #endif /* FX_ENABLE_FAULT_TOLERANT */
524 
525             /* Release media protection.  */
526             FX_UNPROTECT
527 
528             /* Return the error status.  */
529             return(status);
530         }
531     }
532     else
533     {
534 
535 #ifdef FX_ENABLE_FAULT_TOLERANT
536         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
537 #endif /* FX_ENABLE_FAULT_TOLERANT */
538 
539         /* Release media protection.  */
540         FX_UNPROTECT
541 
542         /* Return a size allocated of zero, since no clusters were available.  */
543         *actual_size_allocated =  0;
544 
545         /* Not enough contiguous space on the media.  Return error status.  */
546         return(FX_NO_MORE_SPACE);
547     }
548 
549 #ifdef FX_FAULT_TOLERANT
550 
551     /* Flush the cached individual FAT entries */
552     _fx_utility_FAT_flush(media_ptr);
553 #endif
554 
555     /* Flush the internal logical sector cache.  */
556     status =  _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_sectors_per_FAT), FX_FALSE);
557 
558 #ifdef FX_ENABLE_FAULT_TOLERANT
559     /* Check for a bad status.  */
560     if (status != FX_SUCCESS)
561     {
562 
563         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
564 
565         /* Release media protection.  */
566         FX_UNPROTECT
567 
568         /* Return the bad status.  */
569         return(status);
570     }
571 
572     /* End transaction. */
573     status = _fx_fault_tolerant_transaction_end(media_ptr);
574 #endif /* FX_ENABLE_FAULT_TOLERANT */
575 
576     /* Release media protection.  */
577     FX_UNPROTECT
578 
579     /* Return status to the caller.  */
580     return(status);
581 }
582 
583