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