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 #ifdef FX_ENABLE_FAULT_TOLERANT
33 #include "fx_fault_tolerant.h"
34 #endif /* FX_ENABLE_FAULT_TOLERANT */
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _fx_file_extended_truncate                          PORTABLE C      */
42 /*                                                           6.1          */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    William E. Lamie, Microsoft Corporation                             */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function sets the file to the specified size, if smaller than  */
50 /*    the current file size.  If the new file size is less than the       */
51 /*    current file read/write position, the internal file pointers will   */
52 /*    also be modified.                                                   */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    file_ptr                              File control block pointer    */
57 /*    size                                  New size of the file in bytes */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    return status                                                       */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
66 /*    _fx_fault_tolerant_transaction_start  Start fault tolerant          */
67 /*                                            transaction                 */
68 /*    _fx_fault_tolerant_transaction_end    End fault tolerant transaction*/
69 /*    _fx_fault_tolerant_recover            Recover FAT chain             */
70 /*    _fx_fault_tolerant_reset_log_file     Reset the log file            */
71 /*                                                                        */
72 /*  CALLED BY                                                             */
73 /*                                                                        */
74 /*    Application Code                                                    */
75 /*                                                                        */
76 /*  RELEASE HISTORY                                                       */
77 /*                                                                        */
78 /*    DATE              NAME                      DESCRIPTION             */
79 /*                                                                        */
80 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
81 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
82 /*                                            resulting in version 6.1    */
83 /*                                                                        */
84 /**************************************************************************/
_fx_file_extended_truncate(FX_FILE * file_ptr,ULONG64 size)85 UINT  _fx_file_extended_truncate(FX_FILE *file_ptr, ULONG64 size)
86 {
87 
88 UINT                   status;
89 ULONG                  cluster;
90 ULONG                  contents = 0;
91 ULONG                  bytes_per_cluster;
92 ULONG                  last_cluster;
93 ULONG                  cluster_count;
94 ULONG64                bytes_remaining;
95 FX_MEDIA              *media_ptr;
96 
97 #ifndef FX_DONT_UPDATE_OPEN_FILES
98 ULONG                  open_count;
99 FX_FILE               *search_ptr;
100 #endif
101 
102 #ifdef TX_ENABLE_EVENT_TRACE
103 TX_TRACE_BUFFER_ENTRY *trace_event;
104 ULONG                  trace_timestamp;
105 #endif
106 
107 
108     /* First, determine if the file is still open.  */
109     if (file_ptr -> fx_file_id != FX_FILE_ID)
110     {
111 
112         /* Return the file not open error status.  */
113         return(FX_NOT_OPEN);
114     }
115 
116 #ifndef FX_MEDIA_STATISTICS_DISABLE
117     /* Setup pointer to media structure.  */
118     media_ptr =  file_ptr -> fx_file_media_ptr;
119 
120     /* Increment the number of times this service has been called.  */
121     media_ptr -> fx_media_file_truncates++;
122 #endif
123 
124     /* Setup pointer to associated media control block.  */
125     media_ptr =  file_ptr -> fx_file_media_ptr;
126 
127     /* If trace is enabled, insert this event into the trace buffer.  */
128     FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_TRUNCATE, file_ptr, size, file_ptr -> fx_file_current_file_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp)
129 
130     /* Protect against other threads accessing the media.  */
131     FX_PROTECT
132 
133     /* Make sure this file is open for writing.  */
134     if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE)
135     {
136 
137         /* Release media protection.  */
138         FX_UNPROTECT
139 
140         /* Return the access error exception - a write was attempted from
141            a file opened for reading!  */
142         return(FX_ACCESS_ERROR);
143     }
144 
145     /* Check for write protect at the media level (set by driver).  */
146     if (media_ptr -> fx_media_driver_write_protect)
147     {
148 
149         /* Release media protection.  */
150         FX_UNPROTECT
151 
152         /* Return write protect error.  */
153         return(FX_WRITE_PROTECT);
154     }
155 
156     /* Setup the new file size - if less than the current size.  */
157     if (size < file_ptr -> fx_file_current_file_size)
158     {
159 
160         /* Setup the new size.  */
161         file_ptr -> fx_file_current_file_size =  size;
162 
163         /* Set the modified flag as well.  */
164         file_ptr -> fx_file_modified =  FX_TRUE;
165 
166         /* Update the trace event with the truncated size.  */
167         FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE, 0, 0, 0, size)
168     }
169     else
170     {
171 
172         /* Update the trace event with the truncated size.  */
173         FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE, 0, 0, 0, file_ptr -> fx_file_current_file_size)
174 
175         /* Release media protection.  */
176         FX_UNPROTECT
177 
178         /* Just return, the new size is larger than the current size.  */
179         return(FX_SUCCESS);
180     }
181 
182 #ifndef FX_DONT_UPDATE_OPEN_FILES
183 
184     /* Search the opened files list to see if the same file is opened for reading.  */
185     open_count =  media_ptr -> fx_media_opened_file_count;
186     search_ptr =  media_ptr -> fx_media_opened_file_list;
187     while (open_count)
188     {
189 
190         /* Is this file the same file opened for reading?  */
191         if ((search_ptr != file_ptr) &&
192             (search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
193              file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
194             (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
195              file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset))
196         {
197 
198             /* Yes, the same file is opened for reading.  */
199 
200             /* Setup the new file size.  */
201             search_ptr -> fx_file_current_file_size =  size;
202             search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = size;
203         }
204 
205         /* Adjust the pointer and decrement the search count.  */
206         search_ptr =  search_ptr -> fx_file_opened_next;
207         open_count--;
208     }
209 #endif
210 
211     /* Now check to see if the read/write internal file pointers need
212        to be adjusted.  */
213     if (file_ptr -> fx_file_current_file_offset > file_ptr -> fx_file_current_file_size)
214     {
215 
216         /* Calculate the number of bytes per cluster.  */
217         bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
218             ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
219 
220         /* At this point, we are ready to walk list of clusters to setup the
221            seek position of this file.  */
222         cluster =           file_ptr -> fx_file_first_physical_cluster;
223         bytes_remaining =   size;
224         last_cluster =      0;
225         cluster_count =     0;
226 
227         /* Follow the link of FAT entries.  */
228         while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
229         {
230 
231             /* Increment the number of clusters.  */
232             cluster_count++;
233 
234             /* Read the current cluster entry from the FAT.  */
235             status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
236 
237             /* Check the return value.  */
238             if (status != FX_SUCCESS)
239             {
240 
241                 /* Release media protection.  */
242                 FX_UNPROTECT
243 
244                 /* Return the error status.  */
245                 return(status);
246             }
247 
248             /* Save the last valid cluster.  */
249             last_cluster =  cluster;
250 
251             /* Setup for the next cluster.  */
252             cluster =  contents;
253 
254             /* Determine if this is the last written cluster.  */
255             if (bytes_remaining >= bytes_per_cluster)
256             {
257 
258                 /* Still more seeking, just decrement the working byte offset.  */
259                 bytes_remaining =  bytes_remaining - bytes_per_cluster;
260             }
261             else
262             {
263 
264                 /* This is the cluster that contains the seek position.  */
265                 break;
266             }
267         }
268 
269         /* Check for errors in traversal of the FAT chain.  */
270         if (size > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count)))
271         {
272 
273             /* Release media protection.  */
274             FX_UNPROTECT
275 
276             /* This is an error that suggests a corrupt file.  */
277             return(FX_FILE_CORRUPT);
278         }
279 
280         /* Position the pointers to the new offset.  */
281         file_ptr -> fx_file_current_physical_cluster =  last_cluster;
282         file_ptr -> fx_file_current_relative_cluster =  cluster_count - 1;
283         file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
284             (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
285              ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
286             (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector);
287         file_ptr -> fx_file_current_relative_sector =   (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
288         file_ptr -> fx_file_current_file_offset =       size;
289         file_ptr -> fx_file_current_logical_offset =    (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector);
290     }
291 
292 #ifdef FX_ENABLE_FAULT_TOLERANT
293     if (media_ptr -> fx_media_fault_tolerant_enabled)
294     {
295 
296         /* Start transaction. */
297         _fx_fault_tolerant_transaction_start(media_ptr);
298 
299         /* Copy the new file size into the directory entry.  */
300         file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size;
301 
302         /* Write the directory entry to the media.  */
303         status =  _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry));
304 
305         /* Check for a good status.  */
306         if (status != FX_SUCCESS)
307         {
308 
309             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
310 
311             /* Release media protection.  */
312             FX_UNPROTECT
313 
314             /* Error writing the directory.  */
315             return(status);
316         }
317 
318         /* End transaction. */
319         status = _fx_fault_tolerant_transaction_end(media_ptr);
320 
321         /* Check for a good status.  */
322         if (status != FX_SUCCESS)
323         {
324 
325             /* Release media protection.  */
326             FX_UNPROTECT
327 
328             /* Error writing the directory.  */
329             return(status);
330         }
331 
332         /* Update maximum size used if necessary. */
333         if (size < file_ptr -> fx_file_maximum_size_used)
334         {
335             file_ptr -> fx_file_maximum_size_used = size;
336         }
337     }
338 #endif /* FX_ENABLE_FAULT_TOLERANT */
339 
340     /* Release media protection.  */
341     FX_UNPROTECT
342 
343     /* Truncate is complete, return successful status.  */
344     return(FX_SUCCESS);
345 }
346 
347