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