GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: fx_file_open.c Lines: 137 137 100.0 %
Date: 2026-03-06 18:49:02 Branches: 64 64 100.0 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 * Copyright (c) 2026-present Eclipse ThreadX contributors
4
 *
5
 * This program and the accompanying materials are made available under the
6
 * terms of the MIT License which is available at
7
 * https://opensource.org/licenses/MIT.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 **************************************************************************/
11
12
13
/**************************************************************************/
14
/**************************************************************************/
15
/**                                                                       */
16
/** FileX Component                                                       */
17
/**                                                                       */
18
/**   File                                                                */
19
/**                                                                       */
20
/**************************************************************************/
21
/**************************************************************************/
22
23
#define FX_SOURCE_CODE
24
25
26
/* Include necessary system files.  */
27
28
#include "fx_api.h"
29
#include "fx_system.h"
30
#include "fx_directory.h"
31
#include "fx_file.h"
32
#include "fx_utility.h"
33
34
35
/**************************************************************************/
36
/*                                                                        */
37
/*  FUNCTION                                               RELEASE        */
38
/*                                                                        */
39
/*    _fx_file_open                                       PORTABLE C      */
40
/*                                                           6.1          */
41
/*  AUTHOR                                                                */
42
/*                                                                        */
43
/*    William E. Lamie, Microsoft Corporation                             */
44
/*                                                                        */
45
/*  DESCRIPTION                                                           */
46
/*                                                                        */
47
/*    This function first attempts to find the specified file.  If found, */
48
/*    the open request is validated and the file is opened.  During the   */
49
/*    opening process, all of the FAT entries for this file are examined  */
50
/*    for their integrity.                                                */
51
/*                                                                        */
52
/*  INPUT                                                                 */
53
/*                                                                        */
54
/*    media_ptr                             Media control block pointer   */
55
/*    file_ptr                              File control block pointer    */
56
/*    file_name                             Name pointer                  */
57
/*    open_type                             Type of open requested        */
58
/*                                                                        */
59
/*  OUTPUT                                                                */
60
/*                                                                        */
61
/*    return status                                                       */
62
/*                                                                        */
63
/*  CALLS                                                                 */
64
/*                                                                        */
65
/*    _fx_directory_search                  Search for the file name in   */
66
/*                                          the directory structure       */
67
/*    _fx_utility_FAT_entry_read            Read a FAT entry              */
68
/*                                                                        */
69
/*  CALLED BY                                                             */
70
/*                                                                        */
71
/*    Application Code                                                    */
72
/*                                                                        */
73
/**************************************************************************/
74
18457
UINT  _fx_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, UINT open_type)
75
{
76
77
UINT     status;
78
#ifndef FX_DISABLE_CONSECUTIVE_DETECT
79
UINT     leading_consecutive;
80
#endif /* FX_DISABLE_CONSECUTIVE_DETECT */
81
ULONG    cluster;
82
18457
ULONG    contents = 0;
83
ULONG    open_count;
84
FX_FILE *tail_ptr;
85
FX_FILE *search_ptr;
86
ULONG    bytes_per_cluster;
87
UINT     last_cluster;
88
ULONG    cluster_count;
89
ULONG64  bytes_available;
90
ULONG64  bytes_remaining;
91
ULONG    fat_last;
92
#ifndef FX_DISABLE_FAST_OPEN
93
UINT     fast_open;
94
#endif /* FX_DISABLE_FAST_OPEN */
95
UCHAR    not_a_file_attr;
96
97
98
    /* Check the media to make sure it is open.  */
99
18457
    if (media_ptr -> fx_media_id != FX_MEDIA_ID)
100
    {
101
102
        /* Return the media not opened error.  */
103
136
        return(FX_MEDIA_NOT_OPEN);
104
    }
105
106
#ifndef FX_MEDIA_STATISTICS_DISABLE
107
108
    /* Increment the number of times this service has been called.  */
109
18321
    media_ptr -> fx_media_file_opens++;
110
#endif
111
112
    /* Clear the notify function. */
113
18321
    file_ptr -> fx_file_write_notify = FX_NULL;
114
115
    /* Determine the type of FAT and setup variables accordingly.  */
116
18321
    if (media_ptr -> fx_media_32_bit_FAT)
117
    {
118
3032
        fat_last        = FX_LAST_CLUSTER_1_32;
119
3032
        not_a_file_attr = FX_DIRECTORY | FX_VOLUME;
120
    }
121
    else
122
    {
123
15289
        fat_last        = FX_LAST_CLUSTER_1;
124
15289
        not_a_file_attr = FX_DIRECTORY | FX_VOLUME;
125
    }
126
127
#ifndef FX_DISABLE_FAST_OPEN
128
    /* Determine if a fast open is selected.  */
129
18321
    if (open_type == FX_OPEN_FOR_READ_FAST)
130
    {
131
132
        /* Yes, convert the open type to a standard read.  */
133
2
        open_type =  FX_OPEN_FOR_READ;
134
135
        /* Set the open fast flag.  */
136
2
        fast_open =  FX_TRUE;
137
    }
138
    else
139
    {
140
141
        /* A fast open is not selected, set the flag to false.  */
142
18319
        fast_open =  FX_FALSE;
143
    }
144
#endif /* FX_DISABLE_FAST_OPEN */
145
146
    /* If trace is enabled, register this object.  */
147
    FX_TRACE_OBJECT_REGISTER(FX_TRACE_OBJECT_TYPE_FILE, file_ptr, file_name, 0, 0)
148
149
    /* If trace is enabled, insert this event into the trace buffer.  */
150
    FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_OPEN, media_ptr, file_ptr, file_name, open_type, FX_TRACE_FILE_EVENTS, 0, 0)
151
152
    /* Protect against other threads accessing the media.  */
153
18321
    FX_PROTECT
154
155
    /* Setup file name pointer.  */
156
18321
    file_ptr -> fx_file_dir_entry.fx_dir_entry_name =  file_ptr -> fx_file_name_buffer;
157
18321
    file_ptr -> fx_file_dir_entry.fx_dir_entry_short_name[0] =  0;
158
159
    /* Search the system for the supplied file name.  */
160
18321
    status =  _fx_directory_search(media_ptr, file_name, &(file_ptr -> fx_file_dir_entry), FX_NULL, FX_NULL);
161
162
    /* Determine if the search was successful.  */
163
18321
    if (status != FX_SUCCESS)
164
    {
165
166
        /* Release media protection.  */
167
5
        FX_UNPROTECT
168
169
        /* Return the error code.  */
170
5
        return(status);
171
    }
172
173
    /* Check to make sure the found entry is a file.  */
174
18316
    if (file_ptr -> fx_file_dir_entry.fx_dir_entry_attributes & not_a_file_attr)
175
    {
176
177
        /* Release media protection.  */
178
1
        FX_UNPROTECT
179
180
        /* Return the not a file error code.  */
181
1
        return(FX_NOT_A_FILE);
182
    }
183
184
#ifdef FX_SINGLE_OPEN_LEGACY
185
    /* Check to make sure the access is okay.  */
186
    if (open_type == FX_OPEN_FOR_READ)
187
    {
188
189
        /* Check the list of open files for others open for writing.  */
190
        open_count =  media_ptr -> fx_media_opened_file_count;
191
        search_ptr =  media_ptr -> fx_media_opened_file_list;
192
        while (open_count)
193
        {
194
195
            /* Look at each opened file to see if the same file is opened
196
               for writing.  */
197
            if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
198
                 file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
199
                (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
200
                 file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset) &&
201
                (search_ptr -> fx_file_open_mode))
202
            {
203
204
                /* Release media protection.  */
205
                FX_UNPROTECT
206
207
                /* The file has been opened for writing by a previous call.  */
208
                return(FX_ACCESS_ERROR);
209
            }
210
211
            /* Adjust the pointer and decrement the search count.  */
212
            search_ptr =  search_ptr -> fx_file_opened_next;
213
            open_count--;
214
        }
215
    }
216
    else
217
#else
218
18315
    if (open_type == FX_OPEN_FOR_WRITE)
219
#endif
220
    {
221
222
        /* A open for write request is present, check the file attributes
223
           and the list of open files for any other open instance of
224
           this file.  */
225
17242
        if (media_ptr -> fx_media_driver_write_protect)
226
        {
227
228
            /* Release media protection.  */
229
1
            FX_UNPROTECT
230
231
            /* Return write protect error.  */
232
1
            return(FX_WRITE_PROTECT);
233
        }
234
235
17241
        if (file_ptr -> fx_file_dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_READ_ONLY))
236
        {
237
238
            /* Release media protection.  */
239
1
            FX_UNPROTECT
240
241
            /* Return the not a file error code.  */
242
1
            return(FX_ACCESS_ERROR);
243
        }
244
245
        /* Also search the opened files to see if this file is currently
246
           opened.  */
247
17240
        open_count =  media_ptr -> fx_media_opened_file_count;
248
17240
        search_ptr =  media_ptr -> fx_media_opened_file_list;
249
84298
        while (open_count)
250
        {
251
252
            /* Look at each opened file to see if the same file is already opened.  */
253
#ifdef FX_SINGLE_OPEN_LEGACY
254
            if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
255
                 file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
256
                (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
257
                 file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset))
258
#else
259
            /* Look at each opened file to see if the same file is already opened
260
               for writing.  */
261
67059
            if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
262
67059
                 file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
263
66062
                (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
264
66062
                 file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset) &&
265
7
                (search_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE))
266
#endif
267
            {
268
269
                /* Release media protection.  */
270
1
                FX_UNPROTECT
271
272
                /* The file is currently open.  */
273
1
                return(FX_ACCESS_ERROR);
274
            }
275
276
            /* Adjust the pointer and decrement the search count.  */
277
67058
            search_ptr =  search_ptr -> fx_file_opened_next;
278
67058
            open_count--;
279
        }
280
    }
281
282
    /* At this point, we are ready to walk list of clusters to setup the
283
       initial condition of this file as well as to verify its integrity.  */
284
18312
    cluster =           file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster;
285
18312
    bytes_remaining =   file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size;
286
18312
    bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
287
18312
        ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
288
18312
    file_ptr -> fx_file_current_physical_cluster =  0;
289
290
    /* Check for invalid value.  */
291
18312
    if (bytes_per_cluster == 0)
292
    {
293
294
        /* Release media protection.  */
295
1
        FX_UNPROTECT
296
297
        /* Invalid media, return error.  */
298
1
        return(FX_MEDIA_INVALID);
299
    }
300
301
18311
    last_cluster =      0;
302
18311
    cluster_count =     0;
303
304
#ifndef FX_DISABLE_CONSECUTIVE_DETECT
305
18311
    leading_consecutive = 1;
306
#endif /* FX_DISABLE_CONSECUTIVE_DETECT */
307
18311
    file_ptr -> fx_file_consecutive_cluster = 1;
308
#ifndef FX_DISABLE_FAST_OPEN
309
310
    /* Determine if the file is being open for reading with the fast option.  */
311
18311
    if (fast_open)
312
    {
313
314
        /* Calculate the bytes available.  */
315
2
        bytes_available =  ((bytes_remaining + bytes_per_cluster - 1) / bytes_per_cluster) * bytes_per_cluster;
316
317
    }
318
    else
319
#endif /* FX_DISABLE_FAST_OPEN */
320
    {
321
322
        /* Follow the link of FAT entries.  */
323

36798
        while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
324
        {
325
326
            /* Increment the number of clusters.  */
327
18492
            cluster_count++;
328
329
            /* Read the current cluster entry from the FAT.  */
330
18492
            status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
331
332
            /* Check the return value.  */
333
18492
            if (status != FX_SUCCESS)
334
            {
335
336
                /* Release media protection.  */
337
1
                FX_UNPROTECT
338
339
                /* Return the error status.  */
340
1
                return(status);
341
            }
342
343
            /* Determine if the cluster is invalid (points to itself) or the count exceeds the total number of clusters.  */
344

18491
            if ((cluster == contents) || (cluster_count > media_ptr -> fx_media_total_clusters))
345
            {
346
347
                /* Release media protection.  */
348
2
                FX_UNPROTECT
349
350
                /* Return the bad status.  */
351
2
                return(FX_FAT_READ_ERROR);
352
            }
353
354
#ifndef FX_DISABLE_CONSECUTIVE_DETECT
355
356
            /* Check if present and next clusters are consecutive */
357
18489
            if (cluster + 1 == contents)
358
            {
359
360
                /* Determine if clusters are consecutive so far.  */
361
1115
                if (leading_consecutive)
362
                {
363
364
                    /* Yes, increment the number of leading consecutive clusters.  */
365
1101
                    file_ptr -> fx_file_consecutive_cluster++;
366
                }
367
            }
368
            else
369
            {
370
371
                /* The clusters are no longer consecutive, clear the consecutive flag.  */
372
17374
                leading_consecutive = 0;
373
            }
374
#endif /* FX_DISABLE_CONSECUTIVE_DETECT */
375
376
            /* Save the last valid cluster.  */
377
18489
            last_cluster =  cluster;
378
379
            /* Setup for the next cluster.  */
380
18489
            cluster =  contents;
381
382
            /* Determine if this is the last written cluster.  We need to remember this
383
               for open for writing.  */
384
18489
            if (bytes_remaining > bytes_per_cluster)
385
            {
386
387
                /* Still more written clusters, just decrement the counter.  */
388
6476
                bytes_remaining =  bytes_remaining - bytes_per_cluster;
389
            }
390
12013
            else if (!file_ptr -> fx_file_current_physical_cluster)
391
            {
392
393
                /* Remember this cluster number.  */
394
12011
                file_ptr -> fx_file_current_physical_cluster =  last_cluster;
395
396
                /* Remember the relative cluster.  */
397
12011
                file_ptr -> fx_file_current_relative_cluster =  cluster_count - 1;
398
399
                /* If the remaining bytes exactly fits the cluster size, check for
400
                   a possible adjustment to the next cluster.  */
401

12011
                if ((bytes_remaining == bytes_per_cluster) &&
402
1020
                    (cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
403
                {
404
405
                    /* We need to position to next allocated cluster.  */
406
1
                    file_ptr -> fx_file_current_physical_cluster =  cluster;
407
1
                    file_ptr -> fx_file_current_relative_cluster++;
408
409
                    /* Clear the remaining bytes.  */
410
1
                    bytes_remaining =  0;
411
                }
412
            }
413
        }
414
415
        /* Determine if the number of clusters is large enough to support the
416
           specified file size.  */
417
18306
        bytes_available =  ((ULONG64)media_ptr -> fx_media_bytes_per_sector) *
418
18306
            ((ULONG64)media_ptr -> fx_media_sectors_per_cluster) *
419
18306
            ((ULONG64)cluster_count);
420
421
        /* Check the bytes available in the cluster chain against the directory entry file size.  */
422

18306
        if ((bytes_available < file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size) ||
423
12011
            ((cluster_count) && (contents < fat_last)))
424
        {
425
            /* File is corrupt, release media protection.  */
426
4
            FX_UNPROTECT
427
428
            /* Return a corrupt file error status.  */
429
4
            return(FX_FILE_CORRUPT);
430
        }
431
    }
432
433
    /* The file is okay, populate the file control block and complete the
434
       file open process.  */
435
18304
    file_ptr -> fx_file_id =                        FX_FILE_ID;
436
18304
    file_ptr -> fx_file_name =                      file_ptr -> fx_file_name_buffer;
437
18304
    file_ptr -> fx_file_media_ptr =                 media_ptr;
438
18304
    file_ptr -> fx_file_open_mode =                 open_type;
439
18304
    file_ptr -> fx_file_modified =                  FX_FALSE;
440
18304
    file_ptr -> fx_file_total_clusters =            cluster_count;
441
18304
    file_ptr -> fx_file_first_physical_cluster =    file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster;
442
18304
    file_ptr -> fx_file_last_physical_cluster =     last_cluster;
443
18304
    file_ptr -> fx_file_current_file_size =         file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size;
444
18304
    file_ptr -> fx_file_current_available_size =    bytes_available;
445
18304
    file_ptr -> fx_file_disable_burst_cache =       FX_FALSE;
446
447
    /* Set the current settings based on how the file was opened.  */
448
18304
    if (open_type == FX_OPEN_FOR_READ)
449
    {
450
451
        /* Position the pointers to the beginning of the file.  */
452
1073
        file_ptr -> fx_file_current_physical_cluster =  file_ptr -> fx_file_first_physical_cluster;
453
1073
        file_ptr -> fx_file_current_relative_cluster =  0;
454
1073
        file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
455
1073
            (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) *
456
1073
             ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
457
1073
        file_ptr -> fx_file_current_relative_sector =   0;
458
1073
        file_ptr -> fx_file_current_logical_offset =    0;
459
1073
        file_ptr -> fx_file_current_file_offset =       0;
460
    }
461
    else
462
    {
463
464
        /* Open for writing - position the pointers to the end of the file.  */
465
466
        /* Determine if the remaining bytes fit exactly into the cluster size.  */
467
17231
        if (bytes_remaining == bytes_per_cluster)
468
        {
469
470
            /* Position to the end of the cluster.  */
471
17
            file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
472
17
                (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
473
17
                 ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
474
17
                ((ULONG)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector)));
475
17
            file_ptr -> fx_file_current_relative_sector =   (ULONG)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector));
476
17
            file_ptr -> fx_file_current_file_offset =       file_ptr -> fx_file_current_file_size;
477
17
            file_ptr -> fx_file_current_logical_offset =    media_ptr -> fx_media_bytes_per_sector;
478
        }
479
        else
480
        {
481
482
            /* Position file parameters at end of last cluster allocation.  */
483
17214
            file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
484
17214
                (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
485
17214
                 ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
486
17214
                ((ULONG)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)));
487
17214
            file_ptr -> fx_file_current_relative_sector =   (ULONG)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
488
17214
            file_ptr -> fx_file_current_file_offset =       file_ptr -> fx_file_current_file_size;
489
17214
            file_ptr -> fx_file_current_logical_offset =    (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector);
490
        }
491
    }
492
493
#ifdef FX_ENABLE_FAULT_TOLERANT
494
    /* By default, the whole file is used. */
495
    file_ptr -> fx_file_maximum_size_used = file_ptr -> fx_file_current_file_size;
496
#endif /* FX_ENABLE_FAULT_TOLERANT */
497
498
    /* Place newly opened file on the list of open files for
499
       this media.  First, check for an empty list.  */
500
18304
    if (media_ptr -> fx_media_opened_file_list)
501
    {
502
503
        /* Pickup tail pointer.  */
504
11093
        tail_ptr =  (media_ptr -> fx_media_opened_file_list) -> fx_file_opened_previous;
505
506
        /* Place the new file in the list.  */
507
11093
        (media_ptr -> fx_media_opened_file_list) -> fx_file_opened_previous =  file_ptr;
508
11093
        tail_ptr -> fx_file_opened_next =  file_ptr;
509
510
        /* Setup this file's opened links.  */
511
11093
        file_ptr -> fx_file_opened_previous =  tail_ptr;
512
11093
        file_ptr -> fx_file_opened_next =      media_ptr -> fx_media_opened_file_list;
513
    }
514
    else
515
    {
516
517
        /* The opened media list is empty.  Add the media to empty list.  */
518
7211
        media_ptr -> fx_media_opened_file_list =   file_ptr;
519
7211
        file_ptr ->  fx_file_opened_next =         file_ptr;
520
7211
        file_ptr ->  fx_file_opened_previous =     file_ptr;
521
    }
522
523
    /* Increment the opened file counter.  */
524
18304
    media_ptr -> fx_media_opened_file_count++;
525
526
    /* Release media protection.  */
527
18304
    FX_UNPROTECT
528
529
    /* Open is complete, return successful status.  */
530
18304
    return(FX_SUCCESS);
531
}
532