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