GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: fx_file_extended_best_effort_allocate.c Lines: 111 111 100.0 %
Date: 2024-03-11 05:15:45 Branches: 52 52 100.0 %

Line Branch Exec Source
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
/**************************************************************************/
92
22
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
22
    if (file_ptr -> fx_file_id != FX_FILE_ID)
113
    {
114
115
        /* Return the file not open error status.  */
116
2
        return(FX_NOT_OPEN);
117
    }
118
119
#ifndef FX_MEDIA_STATISTICS_DISABLE
120
    /* Setup pointer to media structure.  */
121
20
    media_ptr =  file_ptr -> fx_file_media_ptr;
122
123
    /* Increment the number of times this service has been called.  */
124
20
    media_ptr -> fx_media_file_best_effort_allocates++;
125
#endif
126
127
    /* Make sure this file is open for writing.  */
128
20
    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
1
        return(FX_ACCESS_ERROR);
134
    }
135
136
    /* Determine if the requested allocation is for zero bytes.  */
137
19
    if (size == 0)
138
    {
139
140
        /* Return a size allocated of zero.  */
141
1
        *actual_size_allocated =  0;
142
143
        /* Return a successful completion - nothing needs to be done.  */
144
1
        return(FX_SUCCESS);
145
    }
146
147
    /* Setup pointer to associated media control block.  */
148
18
    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
18
    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
18
    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
1
        FX_UNPROTECT
171
172
        /* Return write protect error.  */
173
1
        return(FX_WRITE_PROTECT);
174
    }
175
176
    /* Calculate the number of bytes per cluster.  */
177
17
    bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
178
17
        ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
179
180
    /* Check for invalid value.  */
181
17
    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
1
        FX_UNPROTECT
190
191
        /* Invalid media, return error.  */
192
1
        return(FX_MEDIA_INVALID);
193
    }
194
195
    /* Calculate the number of consecutive clusters needed to satisfy this
196
       request.  */
197
16
    clusters =  (ULONG)(((size + bytes_per_cluster - 1) / bytes_per_cluster));
198
199
    /* Determine if cluster count is 0.  */
200
16
    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
1
        FX_UNPROTECT
209
210
        /* Size overflow when rounding to the next cluster, return an error status.  */
211
1
        return(FX_NO_MORE_SPACE);
212
    }
213
214
    /* Determine if there are no available clusters on the media.  */
215
15
    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
1
        FX_UNPROTECT
224
225
        /* Return a size allocated of zero, since no clusters were available.  */
226
1
        *actual_size_allocated =  0;
227
228
        /* Not enough clusters, return an error status.  */
229
1
        return(FX_NO_MORE_SPACE);
230
    }
231
232
    /* Determine if the requested file allocation would exceed the physical limit of the file.  */
233
14
    if (((file_ptr -> fx_file_current_available_size + (((ULONG64) clusters) * ((ULONG64) bytes_per_cluster))) < file_ptr -> fx_file_current_available_size) ||
234
13
        ((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
2
        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
2
        return(FX_NO_MORE_SPACE);
247
    }
248
249
    /* Now we need to find the consecutive clusters.  */
250
12
    found =             FX_FALSE;
251
12
    FAT_index =         FX_FAT_ENTRY_START;
252
12
    maximum_clusters =  0;
253
12
    start_FAT_index =   FAT_index;
254
255
7164
    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
7162
        i =  0;
260
261
262
        do
263
        {
264
265
            /* Read a FAT entry.  */
266
7427
            status =  _fx_utility_FAT_entry_read(media_ptr, (FAT_index + i), &FAT_value);
267
268
            /* Check for a successful status.  */
269
7427
            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
1
                FX_UNPROTECT
278
279
                /* Return the error status.  */
280
1
                return(status);
281
            }
282
283
            /* Determine if the entry is free.  */
284
7426
            if (FAT_value != FX_FREE_CLUSTER)
285
            {
286
7151
                break;
287
            }
288
289
            /* Otherwise, increment the consecutive FAT indices.  */
290
275
            i++;
291

275
        } 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
7161
        if (i > maximum_clusters)
295
        {
296
297
            /* Yes, remember the maximum number of clusters and the starting
298
               cluster.  */
299
11
            maximum_clusters =      i;
300
11
            start_FAT_index =       FAT_index;
301
        }
302
303
        /* Determine if we found enough FAT entries.  */
304
7161
        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
9
            found =  FX_TRUE;
310
9
            break;
311
        }
312
        else
313
        {
314
315
            /* Position to the next possibly free FAT entry.  */
316
7152
            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
11
    if (maximum_clusters)
323
    {
324
325
        /* Yes, there was at least one cluster.  Prepare to return this
326
           to the caller.  */
327
10
        FAT_index =  start_FAT_index;
328
10
        clusters =   maximum_clusters;
329
10
        found =      FX_TRUE;
330
    }
331
332
    /* Determine if we found enough consecutive clusters to satisfy the
333
       request.  */
334
11
    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
271
        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
262
            status =  _fx_utility_FAT_entry_write(media_ptr, FAT_index + i, FAT_index + i + 1);
358
359
            /* Check for a bad status.  */
360
262
            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
1
                FX_UNPROTECT
369
370
                /* Return the error status.  */
371
1
                return(status);
372
            }
373
        }
374
375
376
        /* Now place an EOF in the last cluster entry.  */
377
9
        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
9
        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
1
            FX_UNPROTECT
389
390
            /* Return the error status.  */
391
1
            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
8
        if (file_ptr -> fx_file_total_clusters)
404
        {
405
406
407
            /* Linkup the last cluster.  */
408
6
            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
6
            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
1
                FX_UNPROTECT
421
422
                /* Return the error status.  */
423
1
                return(status);
424
            }
425
426
            /* Determine if we are adding a sector after a write filled the previously
427
               allocated cluster exactly.  */
428
5
            if ((file_ptr -> fx_file_current_relative_sector >=
429
5
                 (media_ptr -> fx_media_sectors_per_cluster - 1)) &&
430
4
                (file_ptr -> fx_file_current_logical_offset >=
431
4
                    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
2
                file_ptr -> fx_file_current_physical_cluster =  FAT_index;
437
2
                file_ptr -> fx_file_current_relative_cluster++;
438
2
                file_ptr -> fx_file_current_relative_sector =   0;
439
2
                file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
440
2
                    (((ULONG64)(FAT_index - FX_FAT_ENTRY_START)) *
441
2
                     ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
442
2
                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
2
            file_ptr -> fx_file_first_physical_cluster =    FAT_index;
451
2
            file_ptr -> fx_file_current_physical_cluster =  file_ptr -> fx_file_first_physical_cluster;
452
2
            file_ptr -> fx_file_current_relative_cluster =  0;
453
2
            file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
454
2
                (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) *
455
2
                 ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
456
2
            file_ptr -> fx_file_current_logical_offset =    0;
457
2
            file_ptr -> fx_file_current_file_offset =       0;
458
459
            /* Setup the consecutive clusters at the beginning of the file.  */
460
2
            file_ptr -> fx_file_consecutive_cluster =       clusters;
461
462
            /* Update the first cluster in the directory entry.  */
463
2
            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
7
        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
7
        file_ptr -> fx_file_current_available_size =
482
7
            file_ptr -> fx_file_current_available_size + bytes_per_cluster * clusters;
483
484
        /* Increment the total clusters for this file.  */
485
7
        file_ptr -> fx_file_total_clusters =
486
7
            file_ptr -> fx_file_total_clusters + clusters;
487
488
        /* Decrease the available clusters on the media.  */
489
7
        media_ptr -> fx_media_available_clusters =
490
7
            media_ptr -> fx_media_available_clusters - clusters;
491
492
        /* Return the actual size allocated.  */
493
7
        *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
7
        status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry));
516
517
        /* Check for a good status.  */
518
7
        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
1
            FX_UNPROTECT
527
528
            /* Return the error status.  */
529
1
            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
1
        FX_UNPROTECT
541
542
        /* Return a size allocated of zero, since no clusters were available.  */
543
1
        *actual_size_allocated =  0;
544
545
        /* Not enough contiguous space on the media.  Return error status.  */
546
1
        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
6
    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
6
    FX_UNPROTECT
578
579
    /* Return status to the caller.  */
580
6
    return(status);
581
}
582