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