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_extended_seek PORTABLE C */
39 /* 6.1.7 */
40 /* AUTHOR */
41 /* */
42 /* William E. Lamie, Microsoft Corporation */
43 /* */
44 /* DESCRIPTION */
45 /* */
46 /* This function positions the internal file pointers to the specified */
47 /* byte offset such that the next read or write operation will be */
48 /* performed there. If the byte offset is greater than the size, the */
49 /* file pointers will be positioned to the end of the file. */
50 /* */
51 /* INPUT */
52 /* */
53 /* file_ptr File control block pointer */
54 /* byte_offset Byte offset into the file */
55 /* */
56 /* OUTPUT */
57 /* */
58 /* return status */
59 /* */
60 /* CALLS */
61 /* */
62 /* _fx_utility_FAT_entry_read Read a FAT entry */
63 /* */
64 /* CALLED BY */
65 /* */
66 /* Application Code */
67 /* */
68 /* RELEASE HISTORY */
69 /* */
70 /* DATE NAME DESCRIPTION */
71 /* */
72 /* 05-19-2020 William E. Lamie Initial Version 6.0 */
73 /* 09-30-2020 William E. Lamie Modified comment(s), */
74 /* resulting in version 6.1 */
75 /* 06-02-2021 Bhupendra Naphade Modified comment(s), fixed */
76 /* relative cluster logic, */
77 /* resulting in version 6.1.7 */
78 /* */
79 /**************************************************************************/
_fx_file_extended_seek(FX_FILE * file_ptr,ULONG64 byte_offset)80 UINT _fx_file_extended_seek(FX_FILE *file_ptr, ULONG64 byte_offset)
81 {
82
83 UINT status;
84 ULONG cluster;
85 ULONG contents = 0;
86 ULONG bytes_per_cluster;
87 ULONG last_cluster;
88 ULONG cluster_count;
89 ULONG64 bytes_remaining;
90 FX_MEDIA *media_ptr;
91
92
93 /* First, determine if the file is still open. */
94 if (file_ptr -> fx_file_id != FX_FILE_ID)
95 {
96
97 /* Return the file not open error status. */
98 return(FX_NOT_OPEN);
99 }
100
101 #ifndef FX_MEDIA_STATISTICS_DISABLE
102 /* Setup pointer to media structure. */
103 media_ptr = file_ptr -> fx_file_media_ptr;
104
105 /* Increment the number of times this service has been called. */
106 media_ptr -> fx_media_file_seeks++;
107 #endif
108
109 /* Setup pointer to associated media control block. */
110 media_ptr = file_ptr -> fx_file_media_ptr;
111
112 /* If trace is enabled, insert this event into the trace buffer. */
113 FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_SEEK, file_ptr, byte_offset, file_ptr -> fx_file_current_file_offset, 0, FX_TRACE_FILE_EVENTS, 0, 0)
114
115 /* Protect against other threads accessing the media. */
116 FX_PROTECT
117
118 /* Check if we actually have to do anything. */
119 if (byte_offset == file_ptr -> fx_file_current_file_offset)
120 {
121
122 /* Release media protection. */
123 FX_UNPROTECT
124
125 /* Seek is complete, return successful status. */
126 return(FX_SUCCESS);
127 }
128
129 /* Calculate the number of bytes per cluster. */
130 bytes_per_cluster = ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
131 ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
132
133 /* Check for invalid value. */
134 if (bytes_per_cluster == 0)
135 {
136
137 /* Release media protection. */
138 FX_UNPROTECT
139
140 /* Invalid media, return error. */
141 return(FX_MEDIA_INVALID);
142 }
143
144 /* See if we need to adjust the byte offset. */
145 if (byte_offset > file_ptr -> fx_file_current_file_size)
146 {
147
148 /* Adjust the byte offset down to the file size. */
149 byte_offset = file_ptr -> fx_file_current_file_size;
150 }
151
152 /* Check if the desired position within the leading consecutive clusters. */
153 if (byte_offset >= (ULONG64)file_ptr -> fx_file_consecutive_cluster * (ULONG64)bytes_per_cluster)
154 {
155
156 /* At this point, we are ready to walk list of clusters to setup the
157 seek position of this file. */
158
159 /* check if byte_offset is greater than where we were left off earlier */
160 if ((ULONG64)file_ptr -> fx_file_current_relative_cluster * (ULONG64)bytes_per_cluster < byte_offset)
161 {
162
163 cluster = file_ptr -> fx_file_current_physical_cluster;
164
165 bytes_remaining = byte_offset -
166 file_ptr -> fx_file_current_relative_cluster * bytes_per_cluster;
167
168 cluster_count = file_ptr -> fx_file_current_relative_cluster;
169 }
170 else
171 {
172
173 cluster = file_ptr -> fx_file_first_physical_cluster +
174 (file_ptr -> fx_file_consecutive_cluster - 1);
175 bytes_remaining = byte_offset -
176 (file_ptr -> fx_file_consecutive_cluster - 1) * bytes_per_cluster;
177 cluster_count = (file_ptr -> fx_file_consecutive_cluster - 1);
178 }
179
180
181 /* Follow the link of FAT entries. */
182 while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
183 {
184
185 /* Increment the number of clusters. */
186 cluster_count++;
187
188 /* Read the current cluster entry from the FAT. */
189 status = _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
190
191 /* Check the return value. */
192 if (status != FX_SUCCESS)
193 {
194
195 /* Release media protection. */
196 FX_UNPROTECT
197
198 /* Return the error status. */
199 return(status);
200 }
201
202 /* Save the last valid cluster. */
203 last_cluster = cluster;
204
205 /* Setup for the next cluster. */
206 cluster = contents;
207
208 /* Determine if this is the last written cluster. */
209 if (bytes_remaining > bytes_per_cluster)
210 {
211
212 /* Still more seeking, just decrement the working byte offset. */
213 bytes_remaining = bytes_remaining - bytes_per_cluster;
214 }
215 else
216 {
217
218 /* Remember this cluster number. */
219 file_ptr -> fx_file_current_physical_cluster = last_cluster;
220
221 /* Remember the relative cluster. */
222 file_ptr -> fx_file_current_relative_cluster = cluster_count - 1;
223
224 /* If the remaining bytes exactly fits the cluster size, check for
225 a possible adjustment to the next cluster. */
226 if ((bytes_remaining == bytes_per_cluster) &&
227 (cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
228 {
229
230 /* We need to position to next allocated cluster. */
231 file_ptr -> fx_file_current_physical_cluster = cluster;
232 file_ptr -> fx_file_current_relative_cluster++;
233
234 /* Clear the remaining bytes. */
235 bytes_remaining = 0;
236 }
237
238 /* This is the cluster that contains the seek position. */
239 break;
240 }
241 }
242
243 /* Check for errors in traversal of the FAT chain. */
244 if (byte_offset > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count)))
245 {
246
247 /* Release media protection. */
248 FX_UNPROTECT
249
250 /* This is an error that suggests a corrupt file. */
251 return(FX_FILE_CORRUPT);
252 }
253 }
254 else
255 {
256
257 /* we should directly access the desired cluster */
258 file_ptr -> fx_file_current_relative_cluster = (ULONG)(byte_offset / bytes_per_cluster);
259
260 file_ptr -> fx_file_current_physical_cluster =
261 file_ptr -> fx_file_first_physical_cluster + file_ptr -> fx_file_current_relative_cluster;
262
263 bytes_remaining = byte_offset % bytes_per_cluster;
264 }
265
266
267 /* Determine if the remaining bytes fit exactly into the cluster size. */
268 if (bytes_remaining == bytes_per_cluster)
269 {
270
271 /* Position to the end of the cluster. */
272 file_ptr -> fx_file_current_logical_sector = (ULONG)(((ULONG)media_ptr -> fx_media_data_sector_start) +
273 (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
274 ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
275 ((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector));
276 file_ptr -> fx_file_current_relative_sector = (UINT)(((bytes_remaining - 1) / (ULONG)media_ptr -> fx_media_bytes_per_sector));
277 file_ptr -> fx_file_current_file_offset = byte_offset;
278 file_ptr -> fx_file_current_logical_offset = media_ptr -> fx_media_bytes_per_sector;
279 }
280 else
281 {
282
283 /* Position the pointers to the new offset. */
284 file_ptr -> fx_file_current_logical_sector = (ULONG)(((ULONG)media_ptr -> fx_media_data_sector_start) +
285 (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
286 ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
287 (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
288 file_ptr -> fx_file_current_relative_sector = (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
289 file_ptr -> fx_file_current_file_offset = byte_offset;
290 file_ptr -> fx_file_current_logical_offset = (ULONG)(bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector));
291 }
292
293 /* Release media protection. */
294 FX_UNPROTECT
295
296 /* Seek is complete, return successful status. */
297 return(FX_SUCCESS);
298 }
299
300