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 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _fx_file_read                                       PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function reads the specified number of bytes (or as many as    */
46 /*    possible into the buffer supplied by the caller.  The actual number */
47 /*    of bytes and the status of the read operation is returned to the    */
48 /*    caller.  In addition, various internal file pointers in the file    */
49 /*    control block are also updated.                                     */
50 /*                                                                        */
51 /*  INPUT                                                                 */
52 /*                                                                        */
53 /*    file_ptr                              File control block pointer    */
54 /*    buffer_ptr                            Buffer pointer                */
55 /*    request_size                          Number of bytes requested     */
56 /*    actual_size                           Pointer to variable for the   */
57 /*                                            number of bytes read        */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    return status                                                       */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
66 /*    _fx_utility_logical_sector_read       Read a logical sector         */
67 /*    _fx_utility_memory_copy               Fast memory copy routine      */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    Application Code                                                    */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
78 /*  09-30-2020     William E. Lamie         Modified comment(s), verified */
79 /*                                            memcpy usage,               */
80 /*                                            resulting in version 6.1    */
81 /*                                                                        */
82 /**************************************************************************/
_fx_file_read(FX_FILE * file_ptr,VOID * buffer_ptr,ULONG request_size,ULONG * actual_size)83 UINT  _fx_file_read(FX_FILE *file_ptr, VOID *buffer_ptr, ULONG request_size, ULONG *actual_size)
84 {
85 
86 UINT                   status;
87 ULONG                  bytes_remaining, i;
88 ULONG                  copy_bytes;
89 UCHAR                 *destination_ptr;
90 ULONG                  cluster, next_cluster;
91 UINT                   sectors;
92 FX_MEDIA              *media_ptr;
93 
94 #ifdef TX_ENABLE_EVENT_TRACE
95 TX_TRACE_BUFFER_ENTRY *trace_event;
96 ULONG                  trace_timestamp;
97 #endif
98 
99 
100     /* First, determine if the file is still open.  */
101     if (file_ptr -> fx_file_id != FX_FILE_ID)
102     {
103 
104         /* Return the file not open error status.  */
105         return(FX_NOT_OPEN);
106     }
107 
108 #ifndef FX_MEDIA_STATISTICS_DISABLE
109     /* Setup pointer to media structure.  */
110     media_ptr =  file_ptr -> fx_file_media_ptr;
111 
112     /* Increment the number of times this service has been called.  */
113     media_ptr -> fx_media_file_reads++;
114 #endif
115 
116     /* Setup pointer to associated media control block.  */
117     media_ptr =  file_ptr -> fx_file_media_ptr;
118 
119     /* If trace is enabled, insert this event into the trace buffer.  */
120     FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_READ, file_ptr, buffer_ptr, request_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp)
121 
122     /* Protect against other threads accessing the media.  */
123     FX_PROTECT
124 
125     /* Next, determine if there is any more bytes to read in the file.  */
126     if (file_ptr -> fx_file_current_file_offset >=
127         file_ptr -> fx_file_current_file_size)
128     {
129 
130         /* Release media protection.  */
131         FX_UNPROTECT
132 
133         /* The file is at the end, return the proper status and set the
134            actual size to 0.  */
135         *actual_size =  0;
136         return(FX_END_OF_FILE);
137     }
138 
139     /* At this point there is something to read.  */
140 
141     /* Setup local buffer pointer.  */
142     destination_ptr =  (UCHAR *)buffer_ptr;
143 
144     /* Determine if there are less bytes left in the file than that specified
145        by the request.  If so, adjust the requested size.  */
146     if ((ULONG64)request_size >
147         (file_ptr -> fx_file_current_file_size - file_ptr -> fx_file_current_file_offset))
148     {
149 
150         /* Adjust the bytes remaining to what's available.  */
151         request_size =  (ULONG)(file_ptr -> fx_file_current_file_size - file_ptr -> fx_file_current_file_offset);
152     }
153 
154     /* Setup the remaining number of bytes to read.  */
155     bytes_remaining =  request_size;
156 
157     /* Loop to read all of the bytes.  */
158     while (bytes_remaining)
159     {
160 
161         /* Determine if a beginning or ending partial read is required.  */
162         if ((file_ptr -> fx_file_current_logical_offset) ||
163             (bytes_remaining < media_ptr -> fx_media_bytes_per_sector))
164         {
165 
166             /* A partial sector read is required.  */
167 
168             /* Read the current logical sector.  */
169             status =  _fx_utility_logical_sector_read(media_ptr,
170                                                       file_ptr -> fx_file_current_logical_sector,
171                                                       media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR);
172 
173             /* Check for good completion status.  */
174             if (status !=  FX_SUCCESS)
175             {
176 
177                 /* Release media protection.  */
178                 FX_UNPROTECT
179 
180                 /* Return the error status.  */
181                 return(status);
182             }
183 
184             /* Copy the appropriate number of bytes into the destination buffer.  */
185             copy_bytes =  media_ptr -> fx_media_bytes_per_sector -
186                 file_ptr -> fx_file_current_logical_offset;
187 
188             /* Check to see if only a portion of the read sector needs to be
189                copied.  */
190             if (copy_bytes > bytes_remaining)
191             {
192 
193                 /* Adjust the number of bytes to copy.  */
194                 copy_bytes =  bytes_remaining;
195             }
196 
197             /* Actually perform the memory copy.  */
198             _fx_utility_memory_copy(((UCHAR *)media_ptr -> fx_media_memory_buffer) + /* Use case of memcpy is verified. */
199                                     file_ptr -> fx_file_current_logical_offset,
200                                     destination_ptr, copy_bytes);
201 
202             /* Increment the logical sector byte offset.  */
203             file_ptr -> fx_file_current_logical_offset =
204                 file_ptr -> fx_file_current_logical_offset + copy_bytes;
205 
206             /* Adjust the remaining bytes to read.  */
207             bytes_remaining =  bytes_remaining - copy_bytes;
208 
209             /* Adjust the pointer to the destination buffer.  */
210             destination_ptr =  destination_ptr + copy_bytes;
211         }
212         else
213         {
214 
215             /* Attempt to read multiple sectors directly into the destination
216                buffer.  */
217 
218             /* Calculate the number of whole sectors to read directly into
219                the destination buffer.  */
220             sectors =  (UINT)(bytes_remaining / media_ptr -> fx_media_bytes_per_sector);
221 
222 
223             next_cluster = cluster = file_ptr -> fx_file_current_physical_cluster;
224             for (i = (media_ptr -> fx_media_sectors_per_cluster -
225                       file_ptr -> fx_file_current_relative_sector); i < sectors; i += media_ptr -> fx_media_sectors_per_cluster)
226             {
227                 status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
228 
229                 /* Determine if an error is present.  */
230                 if ((status != FX_SUCCESS) || (next_cluster < FX_FAT_ENTRY_START) ||
231                     (next_cluster > media_ptr -> fx_media_fat_reserved))
232                 {
233 
234                     /* Release media protection.  */
235                     FX_UNPROTECT
236 
237                     /* Send error message back to caller.  */
238                     if (status != FX_SUCCESS)
239                     {
240                         return(status);
241                     }
242                     else
243                     {
244                         return(FX_FILE_CORRUPT);
245                     }
246                 }
247 
248                 if (next_cluster != cluster + 1)
249                 {
250                     break;
251                 }
252                 else
253                 {
254                     cluster = next_cluster;
255                 }
256             }
257 
258             if (i < sectors)
259             {
260                 sectors = i;
261             }
262 
263             /* Determine if this is a single sector read request.  If so, read the sector so it will
264                come from the internal cache.  */
265             if (sectors == 1)
266             {
267 
268                 /* Read the current logical sector.  */
269                 status =  _fx_utility_logical_sector_read(media_ptr,
270                                                           file_ptr -> fx_file_current_logical_sector,
271                                                           media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DATA_SECTOR);
272 
273                 /* Check for good completion status.  */
274                 if (status !=  FX_SUCCESS)
275                 {
276 
277                     /* Release media protection.  */
278                     FX_UNPROTECT
279 
280                     /* Return the error status.  */
281                     return(status);
282                 }
283 
284                 /* Actually perform the memory copy.  */
285                 _fx_utility_memory_copy((UCHAR *)media_ptr -> fx_media_memory_buffer, destination_ptr, media_ptr -> fx_media_bytes_per_sector); /* Use case of memcpy is verified. */
286             }
287             else
288             {
289 
290                 /* Multiple sector read request.  Read all the sectors at once.  */
291 
292                 /* Perform the data read directly into the user's buffer of
293                    the appropriate number of sectors.  */
294                 media_ptr -> fx_media_disable_burst_cache = file_ptr -> fx_file_disable_burst_cache;
295                 status =  _fx_utility_logical_sector_read(media_ptr, file_ptr -> fx_file_current_logical_sector,
296                                                           destination_ptr, (ULONG) sectors, FX_DATA_SECTOR);
297                 media_ptr -> fx_media_disable_burst_cache = FX_FALSE;
298 
299                 /* Check for good completion status.  */
300                 if (status !=  FX_SUCCESS)
301                 {
302 
303                     /* Release media protection.  */
304                     FX_UNPROTECT
305 
306                     /* Return the error status.  */
307                     return(status);
308                 }
309             }
310 
311             /* Now adjust the various file pointers.  */
312 
313             /* Increment the current logical sector.  Subtract one from
314                the sector count because we are going to use the logical
315                offset to do additional sector/cluster arithmetic below.  */
316             file_ptr -> fx_file_current_logical_sector =
317                 file_ptr -> fx_file_current_logical_sector +
318                 (sectors - 1);
319 
320             /* Move the relative sector and cluster as well.  */
321             file_ptr -> fx_file_current_relative_cluster = file_ptr -> fx_file_current_relative_cluster +
322                 (file_ptr -> fx_file_current_relative_sector + (sectors - 1)) /
323                 media_ptr -> fx_media_sectors_per_cluster;
324 
325             file_ptr -> fx_file_current_relative_sector =
326                 (file_ptr -> fx_file_current_relative_sector +
327                  (sectors - 1)) % media_ptr -> fx_media_sectors_per_cluster;
328 
329             /* Increment the logical sector byte offset.  */
330             file_ptr -> fx_file_current_logical_offset =
331                 media_ptr -> fx_media_bytes_per_sector;
332 
333             file_ptr -> fx_file_current_physical_cluster = cluster;
334 
335             /* Adjust the remaining bytes.  */
336             bytes_remaining =  bytes_remaining -
337                 (((ULONG)media_ptr -> fx_media_bytes_per_sector) * sectors);
338 
339             /* Adjust the pointer to the destination buffer.  */
340             destination_ptr =  destination_ptr +
341                 (((ULONG)media_ptr -> fx_media_bytes_per_sector) * sectors);
342         }
343 
344         /* At this point, we have either read a partial sector or have successfully
345            read one or more whole sectors.  Determine if we are at the end of
346            the current logical sector.  */
347         if (file_ptr -> fx_file_current_logical_offset >=
348             media_ptr -> fx_media_bytes_per_sector)
349         {
350 
351             /* Determine if we are at the exact physical end of the file at the end of reading.  */
352             if ((bytes_remaining == 0) && ((file_ptr -> fx_file_current_file_offset + (ULONG64)request_size) >=
353                                            file_ptr -> fx_file_current_available_size))
354             {
355 
356                 /* Skip the following file parameter adjustments.  The next write will
357                    detect the logical offset out of the range of the sector and reset
358                    all of the pertinent information.  */
359                 break;
360             }
361 
362             /* We need to move to the next logical sector, but first
363                determine if the next logical sector is within the same
364                cluster.  */
365 
366             /* Increment the current relative sector in the cluster.  */
367             file_ptr -> fx_file_current_relative_sector++;
368 
369             /* Determine if this is in a new cluster.  */
370             if (file_ptr -> fx_file_current_relative_sector >=
371                 media_ptr -> fx_media_sectors_per_cluster)
372             {
373 
374                 /* Read the FAT entry of the current cluster to find
375                    the next cluster.  */
376                 status =  _fx_utility_FAT_entry_read(media_ptr,
377                                                      file_ptr -> fx_file_current_physical_cluster, &next_cluster);
378 
379                 /* Determine if an error is present.  */
380                 if ((status != FX_SUCCESS) || (next_cluster < FX_FAT_ENTRY_START) ||
381                     (next_cluster > media_ptr -> fx_media_fat_reserved))
382                 {
383 
384                     /* Release media protection.  */
385                     FX_UNPROTECT
386 
387                     /* Send error message back to caller.  */
388                     if (status != FX_SUCCESS)
389                     {
390                         return(status);
391                     }
392                     else
393                     {
394                         return(FX_FILE_CORRUPT);
395                     }
396                 }
397 
398                 /* Otherwise, we have a new cluster.  Save it in the file
399                    control block and calculate a new logical sector value.  */
400                 file_ptr -> fx_file_current_physical_cluster =  next_cluster;
401                 file_ptr -> fx_file_current_relative_cluster++;
402                 file_ptr -> fx_file_current_logical_sector = ((ULONG)media_ptr -> fx_media_data_sector_start) +
403                     ((((ULONG64)next_cluster) - FX_FAT_ENTRY_START) *
404                      ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
405                 file_ptr -> fx_file_current_relative_sector =  0;
406             }
407             else
408             {
409 
410                 /* Still within the same cluster so just increment the
411                    logical sector.  */
412                 file_ptr -> fx_file_current_logical_sector++;
413             }
414 
415             /* In either case, we are now positioned at a new sector so
416                clear the logical sector offset.  */
417             file_ptr -> fx_file_current_logical_offset =  0;
418         }
419     }
420 
421     /* Adjust the current file offset accordingly.  */
422     file_ptr -> fx_file_current_file_offset =
423         file_ptr -> fx_file_current_file_offset + (ULONG64)request_size;
424 
425     /* Store the number of bytes actually read.  */
426     *actual_size =  request_size;
427 
428     /* Update the trace event with the bytes read.  */
429     FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_READ, 0, 0, 0, request_size)
430 
431     /* Update the last accessed date.  */
432     file_ptr -> fx_file_dir_entry.fx_dir_entry_last_accessed_date =  _fx_system_date;
433 
434     /* Release media protection.  */
435     FX_UNPROTECT
436 
437     /* Return a successful status to the caller.  */
438     return(FX_SUCCESS);
439 }
440 
441