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