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