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