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_directory.h"
30 #include "fx_file.h"
31 #include "fx_utility.h"
32 
33 
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _fx_file_open                                       PORTABLE C      */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function first attempts to find the specified file.  If found, */
47 /*    the open request is validated and the file is opened.  During the   */
48 /*    opening process, all of the FAT entries for this file are examined  */
49 /*    for their integrity.                                                */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    media_ptr                             Media control block pointer   */
54 /*    file_ptr                              File control block pointer    */
55 /*    file_name                             Name pointer                  */
56 /*    open_type                             Type of open requested        */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    return status                                                       */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _fx_directory_search                  Search for the file name in   */
65 /*                                          the directory structure       */
66 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
67 /*                                                                        */
68 /*  CALLED BY                                                             */
69 /*                                                                        */
70 /*    Application Code                                                    */
71 /*                                                                        */
72 /*  RELEASE HISTORY                                                       */
73 /*                                                                        */
74 /*    DATE              NAME                      DESCRIPTION             */
75 /*                                                                        */
76 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
77 /*  09-30-2020     William E. Lamie         Modified comment(s), and      */
78 /*                                            added conditional to        */
79 /*                                            disable fast open and       */
80 /*                                            consecutive detect,         */
81 /*                                            resulting in version 6.1    */
82 /*                                                                        */
83 /**************************************************************************/
_fx_file_open(FX_MEDIA * media_ptr,FX_FILE * file_ptr,CHAR * file_name,UINT open_type)84 UINT  _fx_file_open(FX_MEDIA *media_ptr, FX_FILE *file_ptr, CHAR *file_name, UINT open_type)
85 {
86 
87 UINT     status;
88 #ifndef FX_DISABLE_CONSECUTIVE_DETECT
89 UINT     leading_consecutive;
90 #endif /* FX_DISABLE_CONSECUTIVE_DETECT */
91 ULONG    cluster;
92 ULONG    contents = 0;
93 ULONG    open_count;
94 FX_FILE *tail_ptr;
95 FX_FILE *search_ptr;
96 ULONG    bytes_per_cluster;
97 UINT     last_cluster;
98 ULONG    cluster_count;
99 ULONG64  bytes_available;
100 ULONG64  bytes_remaining;
101 ULONG    fat_last;
102 #ifndef FX_DISABLE_FAST_OPEN
103 UINT     fast_open;
104 #endif /* FX_DISABLE_FAST_OPEN */
105 UCHAR    not_a_file_attr;
106 
107 
108     /* Check the media to make sure it is open.  */
109     if (media_ptr -> fx_media_id != FX_MEDIA_ID)
110     {
111 
112         /* Return the media not opened error.  */
113         return(FX_MEDIA_NOT_OPEN);
114     }
115 
116 #ifndef FX_MEDIA_STATISTICS_DISABLE
117 
118     /* Increment the number of times this service has been called.  */
119     media_ptr -> fx_media_file_opens++;
120 #endif
121 
122     /* Clear the notify function. */
123     file_ptr -> fx_file_write_notify = FX_NULL;
124 
125     /* Determine the type of FAT and setup variables accordingly.  */
126     if (media_ptr -> fx_media_32_bit_FAT)
127     {
128         fat_last        = FX_LAST_CLUSTER_1_32;
129         not_a_file_attr = FX_DIRECTORY | FX_VOLUME;
130     }
131     else
132     {
133         fat_last        = FX_LAST_CLUSTER_1;
134         not_a_file_attr = FX_DIRECTORY | FX_VOLUME;
135     }
136 
137 #ifndef FX_DISABLE_FAST_OPEN
138     /* Determine if a fast open is selected.  */
139     if (open_type == FX_OPEN_FOR_READ_FAST)
140     {
141 
142         /* Yes, convert the open type to a standard read.  */
143         open_type =  FX_OPEN_FOR_READ;
144 
145         /* Set the open fast flag.  */
146         fast_open =  FX_TRUE;
147     }
148     else
149     {
150 
151         /* A fast open is not selected, set the flag to false.  */
152         fast_open =  FX_FALSE;
153     }
154 #endif /* FX_DISABLE_FAST_OPEN */
155 
156     /* If trace is enabled, register this object.  */
157     FX_TRACE_OBJECT_REGISTER(FX_TRACE_OBJECT_TYPE_FILE, file_ptr, file_name, 0, 0)
158 
159     /* If trace is enabled, insert this event into the trace buffer.  */
160     FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_OPEN, media_ptr, file_ptr, file_name, open_type, FX_TRACE_FILE_EVENTS, 0, 0)
161 
162     /* Protect against other threads accessing the media.  */
163     FX_PROTECT
164 
165     /* Setup file name pointer.  */
166     file_ptr -> fx_file_dir_entry.fx_dir_entry_name =  file_ptr -> fx_file_name_buffer;
167     file_ptr -> fx_file_dir_entry.fx_dir_entry_short_name[0] =  0;
168 
169     /* Search the system for the supplied file name.  */
170     status =  _fx_directory_search(media_ptr, file_name, &(file_ptr -> fx_file_dir_entry), FX_NULL, FX_NULL);
171 
172     /* Determine if the search was successful.  */
173     if (status != FX_SUCCESS)
174     {
175 
176         /* Release media protection.  */
177         FX_UNPROTECT
178 
179         /* Return the error code.  */
180         return(status);
181     }
182 
183     /* Check to make sure the found entry is a file.  */
184     if (file_ptr -> fx_file_dir_entry.fx_dir_entry_attributes & not_a_file_attr)
185     {
186 
187         /* Release media protection.  */
188         FX_UNPROTECT
189 
190         /* Return the not a file error code.  */
191         return(FX_NOT_A_FILE);
192     }
193 
194 #ifdef FX_SINGLE_OPEN_LEGACY
195     /* Check to make sure the access is okay.  */
196     if (open_type == FX_OPEN_FOR_READ)
197     {
198 
199         /* Check the list of open files for others open for writing.  */
200         open_count =  media_ptr -> fx_media_opened_file_count;
201         search_ptr =  media_ptr -> fx_media_opened_file_list;
202         while (open_count)
203         {
204 
205             /* Look at each opened file to see if the same file is opened
206                for writing.  */
207             if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
208                  file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
209                 (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
210                  file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset) &&
211                 (search_ptr -> fx_file_open_mode))
212             {
213 
214                 /* Release media protection.  */
215                 FX_UNPROTECT
216 
217                 /* The file has been opened for writing by a previous call.  */
218                 return(FX_ACCESS_ERROR);
219             }
220 
221             /* Adjust the pointer and decrement the search count.  */
222             search_ptr =  search_ptr -> fx_file_opened_next;
223             open_count--;
224         }
225     }
226     else
227 #else
228     if (open_type == FX_OPEN_FOR_WRITE)
229 #endif
230     {
231 
232         /* A open for write request is present, check the file attributes
233            and the list of open files for any other open instance of
234            this file.  */
235         if (media_ptr -> fx_media_driver_write_protect)
236         {
237 
238             /* Release media protection.  */
239             FX_UNPROTECT
240 
241             /* Return write protect error.  */
242             return(FX_WRITE_PROTECT);
243         }
244 
245         if (file_ptr -> fx_file_dir_entry.fx_dir_entry_attributes & (UCHAR)(FX_READ_ONLY))
246         {
247 
248             /* Release media protection.  */
249             FX_UNPROTECT
250 
251             /* Return the not a file error code.  */
252             return(FX_ACCESS_ERROR);
253         }
254 
255         /* Also search the opened files to see if this file is currently
256            opened.  */
257         open_count =  media_ptr -> fx_media_opened_file_count;
258         search_ptr =  media_ptr -> fx_media_opened_file_list;
259         while (open_count)
260         {
261 
262             /* Look at each opened file to see if the same file is already opened.  */
263 #ifdef FX_SINGLE_OPEN_LEGACY
264             if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
265                  file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
266                 (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
267                  file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset))
268 #else
269             /* Look at each opened file to see if the same file is already opened
270                for writing.  */
271             if ((search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
272                  file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
273                 (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
274                  file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset) &&
275                 (search_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE))
276 #endif
277             {
278 
279                 /* Release media protection.  */
280                 FX_UNPROTECT
281 
282                 /* The file is currently open.  */
283                 return(FX_ACCESS_ERROR);
284             }
285 
286             /* Adjust the pointer and decrement the search count.  */
287             search_ptr =  search_ptr -> fx_file_opened_next;
288             open_count--;
289         }
290     }
291 
292     /* At this point, we are ready to walk list of clusters to setup the
293        initial condition of this file as well as to verify its integrity.  */
294     cluster =           file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster;
295     bytes_remaining =   file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size;
296     bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
297         ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
298     file_ptr -> fx_file_current_physical_cluster =  0;
299 
300     /* Check for invalid value.  */
301     if (bytes_per_cluster == 0)
302     {
303 
304         /* Release media protection.  */
305         FX_UNPROTECT
306 
307         /* Invalid media, return error.  */
308         return(FX_MEDIA_INVALID);
309     }
310 
311     last_cluster =      0;
312     cluster_count =     0;
313 
314 #ifndef FX_DISABLE_CONSECUTIVE_DETECT
315     leading_consecutive = 1;
316 #endif /* FX_DISABLE_CONSECUTIVE_DETECT */
317     file_ptr -> fx_file_consecutive_cluster = 1;
318 #ifndef FX_DISABLE_FAST_OPEN
319 
320     /* Determine if the file is being open for reading with the fast option.  */
321     if (fast_open)
322     {
323 
324         /* Calculate the bytes available.  */
325         bytes_available =  ((bytes_remaining + bytes_per_cluster - 1) / bytes_per_cluster) * bytes_per_cluster;
326 
327     }
328     else
329 #endif /* FX_DISABLE_FAST_OPEN */
330     {
331 
332         /* Follow the link of FAT entries.  */
333         while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
334         {
335 
336             /* Increment the number of clusters.  */
337             cluster_count++;
338 
339             /* Read the current cluster entry from the FAT.  */
340             status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
341 
342             /* Check the return value.  */
343             if (status != FX_SUCCESS)
344             {
345 
346                 /* Release media protection.  */
347                 FX_UNPROTECT
348 
349                 /* Return the error status.  */
350                 return(status);
351             }
352 
353             /* Determine if the cluster is invalid (points to itself) or the count exceeds the total number of clusters.  */
354             if ((cluster == contents) || (cluster_count > media_ptr -> fx_media_total_clusters))
355             {
356 
357                 /* Release media protection.  */
358                 FX_UNPROTECT
359 
360                 /* Return the bad status.  */
361                 return(FX_FAT_READ_ERROR);
362             }
363 
364 #ifndef FX_DISABLE_CONSECUTIVE_DETECT
365 
366             /* Check if present and next clusters are consecutive */
367             if (cluster + 1 == contents)
368             {
369 
370                 /* Determine if clusters are consecutive so far.  */
371                 if (leading_consecutive)
372                 {
373 
374                     /* Yes, increment the number of leading consecutive clusters.  */
375                     file_ptr -> fx_file_consecutive_cluster++;
376                 }
377             }
378             else
379             {
380 
381                 /* The clusters are no longer consecutive, clear the consecutive flag.  */
382                 leading_consecutive = 0;
383             }
384 #endif /* FX_DISABLE_CONSECUTIVE_DETECT */
385 
386             /* Save the last valid cluster.  */
387             last_cluster =  cluster;
388 
389             /* Setup for the next cluster.  */
390             cluster =  contents;
391 
392             /* Determine if this is the last written cluster.  We need to remember this
393                for open for writing.  */
394             if (bytes_remaining > bytes_per_cluster)
395             {
396 
397                 /* Still more written clusters, just decrement the counter.  */
398                 bytes_remaining =  bytes_remaining - bytes_per_cluster;
399             }
400             else if (!file_ptr -> fx_file_current_physical_cluster)
401             {
402 
403                 /* Remember this cluster number.  */
404                 file_ptr -> fx_file_current_physical_cluster =  last_cluster;
405 
406                 /* Remember the relative cluster.  */
407                 file_ptr -> fx_file_current_relative_cluster =  cluster_count - 1;
408 
409                 /* If the remaining bytes exactly fits the cluster size, check for
410                    a possible adjustment to the next cluster.  */
411                 if ((bytes_remaining == bytes_per_cluster) &&
412                     (cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
413                 {
414 
415                     /* We need to position to next allocated cluster.  */
416                     file_ptr -> fx_file_current_physical_cluster =  cluster;
417                     file_ptr -> fx_file_current_relative_cluster++;
418 
419                     /* Clear the remaining bytes.  */
420                     bytes_remaining =  0;
421                 }
422             }
423         }
424 
425         /* Determine if the number of clusters is large enough to support the
426            specified file size.  */
427         bytes_available =  ((ULONG64)media_ptr -> fx_media_bytes_per_sector) *
428             ((ULONG64)media_ptr -> fx_media_sectors_per_cluster) *
429             ((ULONG64)cluster_count);
430 
431         /* Check the bytes available in the cluster chain against the directory entry file size.  */
432         if ((bytes_available < file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size) ||
433             ((cluster_count) && (contents < fat_last)))
434         {
435             /* File is corrupt, release media protection.  */
436             FX_UNPROTECT
437 
438             /* Return a corrupt file error status.  */
439             return(FX_FILE_CORRUPT);
440         }
441     }
442 
443     /* The file is okay, populate the file control block and complete the
444        file open process.  */
445     file_ptr -> fx_file_id =                        FX_FILE_ID;
446     file_ptr -> fx_file_name =                      file_ptr -> fx_file_name_buffer;
447     file_ptr -> fx_file_media_ptr =                 media_ptr;
448     file_ptr -> fx_file_open_mode =                 open_type;
449     file_ptr -> fx_file_modified =                  FX_FALSE;
450     file_ptr -> fx_file_total_clusters =            cluster_count;
451     file_ptr -> fx_file_first_physical_cluster =    file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster;
452     file_ptr -> fx_file_last_physical_cluster =     last_cluster;
453     file_ptr -> fx_file_current_file_size =         file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size;
454     file_ptr -> fx_file_current_available_size =    bytes_available;
455     file_ptr -> fx_file_disable_burst_cache =       FX_FALSE;
456 
457     /* Set the current settings based on how the file was opened.  */
458     if (open_type == FX_OPEN_FOR_READ)
459     {
460 
461         /* Position the pointers to the beginning of the file.  */
462         file_ptr -> fx_file_current_physical_cluster =  file_ptr -> fx_file_first_physical_cluster;
463         file_ptr -> fx_file_current_relative_cluster =  0;
464         file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
465             (((ULONG64)(file_ptr -> fx_file_first_physical_cluster - FX_FAT_ENTRY_START)) *
466              ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
467         file_ptr -> fx_file_current_relative_sector =   0;
468         file_ptr -> fx_file_current_logical_offset =    0;
469         file_ptr -> fx_file_current_file_offset =       0;
470     }
471     else
472     {
473 
474         /* Open for writing - position the pointers to the end of the file.  */
475 
476         /* Determine if the remaining bytes fit exactly into the cluster size.  */
477         if (bytes_remaining == bytes_per_cluster)
478         {
479 
480             /* Position to the end of the cluster.  */
481             file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
482                 (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
483                  ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
484                 ((ULONG)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector)));
485             file_ptr -> fx_file_current_relative_sector =   (ULONG)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector));
486             file_ptr -> fx_file_current_file_offset =       file_ptr -> fx_file_current_file_size;
487             file_ptr -> fx_file_current_logical_offset =    media_ptr -> fx_media_bytes_per_sector;
488         }
489         else
490         {
491 
492             /* Position file parameters at end of last cluster allocation.  */
493             file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
494                 (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
495                  ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
496                 ((ULONG)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector)));
497             file_ptr -> fx_file_current_relative_sector =   (ULONG)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
498             file_ptr -> fx_file_current_file_offset =       file_ptr -> fx_file_current_file_size;
499             file_ptr -> fx_file_current_logical_offset =    (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector);
500         }
501     }
502 
503 #ifdef FX_ENABLE_FAULT_TOLERANT
504     /* By default, the whole file is used. */
505     file_ptr -> fx_file_maximum_size_used = file_ptr -> fx_file_current_file_size;
506 #endif /* FX_ENABLE_FAULT_TOLERANT */
507 
508     /* Place newly opened file on the list of open files for
509        this media.  First, check for an empty list.  */
510     if (media_ptr -> fx_media_opened_file_list)
511     {
512 
513         /* Pickup tail pointer.  */
514         tail_ptr =  (media_ptr -> fx_media_opened_file_list) -> fx_file_opened_previous;
515 
516         /* Place the new file in the list.  */
517         (media_ptr -> fx_media_opened_file_list) -> fx_file_opened_previous =  file_ptr;
518         tail_ptr -> fx_file_opened_next =  file_ptr;
519 
520         /* Setup this file's opened links.  */
521         file_ptr -> fx_file_opened_previous =  tail_ptr;
522         file_ptr -> fx_file_opened_next =      media_ptr -> fx_media_opened_file_list;
523     }
524     else
525     {
526 
527         /* The opened media list is empty.  Add the media to empty list.  */
528         media_ptr -> fx_media_opened_file_list =   file_ptr;
529         file_ptr ->  fx_file_opened_next =         file_ptr;
530         file_ptr ->  fx_file_opened_previous =     file_ptr;
531     }
532 
533     /* Increment the opened file counter.  */
534     media_ptr -> fx_media_opened_file_count++;
535 
536     /* Release media protection.  */
537     FX_UNPROTECT
538 
539     /* Open is complete, return successful status.  */
540     return(FX_SUCCESS);
541 }
542 
543