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_release                  PORTABLE C      */
42 /*                                                           6.1.3        */
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.  Any unused clusters are released back to the     */
53 /*    media.                                                              */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    file_ptr                              File control block pointer    */
58 /*    size                                  New size of the file in bytes */
59 /*                                                                        */
60 /*  OUTPUT                                                                */
61 /*                                                                        */
62 /*    return status                                                       */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _fx_directory_entry_write             Write directory entry         */
67 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
68 /*    _fx_utility_FAT_entry_write           Write a FAT entry             */
69 /*    _fx_utility_FAT_flush                 Flush written FAT entries     */
70 /*    _fx_fault_tolerant_transaction_start  Start fault tolerant          */
71 /*                                            transaction                 */
72 /*    _fx_fault_tolerant_transaction_end    End fault tolerant transaction*/
73 /*    _fx_fault_tolerant_recover            Recover FAT chain             */
74 /*    _fx_fault_tolerant_reset_log_file     Reset the log file            */
75 /*    _fx_fault_tolerant_set_FAT_chain      Set data of FAT chain         */
76 /*                                                                        */
77 /*  CALLED BY                                                             */
78 /*                                                                        */
79 /*    Application Code                                                    */
80 /*                                                                        */
81 /*  RELEASE HISTORY                                                       */
82 /*                                                                        */
83 /*    DATE              NAME                      DESCRIPTION             */
84 /*                                                                        */
85 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
86 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
87 /*                                            resulting in version 6.1    */
88 /*  12-31-2020     William E. Lamie         Modified comment(s), fixed    */
89 /*                                            available cluster issue,    */
90 /*                                            resulting in version 6.1.3  */
91 /*                                                                        */
92 /**************************************************************************/
_fx_file_extended_truncate_release(FX_FILE * file_ptr,ULONG64 size)93 UINT  _fx_file_extended_truncate_release(FX_FILE *file_ptr, ULONG64 size)
94 {
95 
96 UINT                   status;
97 ULONG                  cluster;
98 ULONG                  contents;
99 ULONG                  bytes_per_cluster;
100 ULONG                  last_cluster;
101 ULONG                  cluster_count;
102 ULONG64                bytes_remaining;
103 FX_MEDIA              *media_ptr;
104 
105 
106 #ifndef FX_DONT_UPDATE_OPEN_FILES
107 ULONG                  open_count;
108 FX_FILE               *search_ptr;
109 #endif
110 
111 #ifdef TX_ENABLE_EVENT_TRACE
112 TX_TRACE_BUFFER_ENTRY *trace_event;
113 ULONG                  trace_timestamp;
114 #endif
115 
116 
117     /* First, determine if the file is still open.  */
118     if (file_ptr -> fx_file_id != FX_FILE_ID)
119     {
120 
121         /* Return the file not open error status.  */
122         return(FX_NOT_OPEN);
123     }
124 
125 #ifndef FX_MEDIA_STATISTICS_DISABLE
126     /* Setup pointer to media structure.  */
127     media_ptr =  file_ptr -> fx_file_media_ptr;
128 
129     /* Increment the number of times this service has been called.  */
130     media_ptr -> fx_media_file_truncate_releases++;
131 #endif
132 
133     /* Setup pointer to associated media control block.  */
134     media_ptr =  file_ptr -> fx_file_media_ptr;
135 
136     /* If trace is enabled, insert this event into the trace buffer.  */
137     FX_TRACE_IN_LINE_INSERT(FX_TRACE_FILE_TRUNCATE_RELEASE, file_ptr, size, file_ptr -> fx_file_current_file_size, 0, FX_TRACE_FILE_EVENTS, &trace_event, &trace_timestamp)
138 
139     /* Protect against other threads accessing the media.  */
140     FX_PROTECT
141 
142 #ifdef FX_ENABLE_FAULT_TOLERANT
143     /* Start transaction. */
144     _fx_fault_tolerant_transaction_start(media_ptr);
145 #endif /* FX_ENABLE_FAULT_TOLERANT */
146 
147     /* Make sure this file is open for writing.  */
148     if (file_ptr -> fx_file_open_mode != FX_OPEN_FOR_WRITE)
149     {
150 
151 #ifdef FX_ENABLE_FAULT_TOLERANT
152         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
153 #endif /* FX_ENABLE_FAULT_TOLERANT */
154 
155         /* Release media protection.  */
156         FX_UNPROTECT
157 
158         /* Return the access error exception - a write was attempted from
159            a file opened for reading!  */
160         return(FX_ACCESS_ERROR);
161     }
162 
163     /* Check for write protect at the media level (set by driver).  */
164     if (media_ptr -> fx_media_driver_write_protect)
165     {
166 
167 #ifdef FX_ENABLE_FAULT_TOLERANT
168         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
169 #endif /* FX_ENABLE_FAULT_TOLERANT */
170 
171         /* Release media protection.  */
172         FX_UNPROTECT
173 
174         /* Return write protect error.  */
175         return(FX_WRITE_PROTECT);
176     }
177 
178     /* Calculate the number of bytes per cluster.  */
179     bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
180         ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
181 
182     /* Check for invalid value.  */
183     if (bytes_per_cluster == 0)
184     {
185 
186 #ifdef FX_ENABLE_FAULT_TOLERANT
187         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
188 #endif /* FX_ENABLE_FAULT_TOLERANT */
189 
190         /* Release media protection.  */
191         FX_UNPROTECT
192 
193         /* Invalid media, return error.  */
194         return(FX_MEDIA_INVALID);
195     }
196 
197     /* Setup the new file available size - if less than the current available size.  */
198     if (size < file_ptr -> fx_file_current_available_size)
199     {
200 
201         /* Yes, the file needs to be truncated.  */
202 
203         /* Update the available file size.  */
204         file_ptr -> fx_file_current_available_size =  ((size + bytes_per_cluster - 1) / bytes_per_cluster) * bytes_per_cluster;
205 
206         /* Is the new available size less than the actual file size?  */
207         if (size < file_ptr -> fx_file_current_file_size)
208         {
209 
210             /* Setup the new file size.  */
211             file_ptr -> fx_file_current_file_size =  size;
212 
213             /* Set the modified flag.  */
214             file_ptr -> fx_file_modified =  FX_TRUE;
215 
216             /* Copy the new file size into the directory entry.  */
217             file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size =  size;
218 
219             /* Set the first cluster to NULL.  */
220             if (size == 0)
221             {
222 
223                 /* Yes, the first cluster needs to be cleared since the entire
224                    file is going to be truncated.  */
225                 file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster =  FX_NULL;
226             }
227 
228             /* Write the directory entry to the media.  */
229             status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry));
230 
231             /* Check for a good status.  */
232             if (status != FX_SUCCESS)
233             {
234 
235 #ifdef FX_ENABLE_FAULT_TOLERANT
236                 FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
237 #endif /* FX_ENABLE_FAULT_TOLERANT */
238 
239                 /* Release media protection.  */
240                 FX_UNPROTECT
241 
242                 /* Error writing the directory.  */
243                 return(status);
244             }
245         }
246 
247         /* Update the trace event with the truncated size.  */
248         FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE_RELEASE, 0, 0, 0, file_ptr -> fx_file_current_file_size)
249     }
250     else
251     {
252 
253         /* Update the trace event with the truncated size.  */
254         FX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, FX_TRACE_FILE_TRUNCATE_RELEASE, 0, 0, 0, file_ptr -> fx_file_current_file_size)
255 
256 #ifdef FX_ENABLE_FAULT_TOLERANT
257         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
258 #endif /* FX_ENABLE_FAULT_TOLERANT */
259 
260         /* Release media protection.  */
261         FX_UNPROTECT
262 
263         /* Just return, the truncate size is larger than the available size.  */
264         return(FX_SUCCESS);
265     }
266 
267     /* Calculate the number of bytes per cluster.  */
268     bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
269         ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
270 
271     /* Now check to see if the read/write internal file pointers need
272        to be adjusted.  */
273     if (file_ptr -> fx_file_current_file_offset > file_ptr -> fx_file_current_file_size)
274     {
275 
276 
277         /* At this point, we are ready to walk list of clusters to setup the
278            seek position of this file.  */
279         cluster =           file_ptr -> fx_file_first_physical_cluster;
280         bytes_remaining =   size;
281         last_cluster =      0;
282         cluster_count =     0;
283 
284         /* Follow the link of FAT entries.  */
285         while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
286         {
287 
288             /* Increment the number of clusters.  */
289             cluster_count++;
290 
291 
292             /* Read the current cluster entry from the FAT.  */
293             status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
294 
295             /* Check the return value.  */
296             if (status != FX_SUCCESS)
297             {
298 
299 #ifdef FX_ENABLE_FAULT_TOLERANT
300                 FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
301 #endif /* FX_ENABLE_FAULT_TOLERANT */
302 
303                 /* Release media protection.  */
304                 FX_UNPROTECT
305 
306                 /* Return the error status.  */
307                 return(status);
308             }
309 
310             /* Save the last valid cluster.  */
311             last_cluster =  cluster;
312 
313             /* Setup for the next cluster.  */
314             cluster =  contents;
315 
316             /* Determine if this is the last written cluster.  */
317             if (bytes_remaining >= bytes_per_cluster)
318             {
319 
320                 /* Still more seeking, just decrement the working byte offset.  */
321                 bytes_remaining =  bytes_remaining - bytes_per_cluster;
322             }
323             else
324             {
325 
326                 /* This is the cluster that contains the seek position.  */
327                 break;
328             }
329         }
330 
331         /* Check for errors in traversal of the FAT chain.  */
332         if (size > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count)))
333         {
334 
335 #ifdef FX_ENABLE_FAULT_TOLERANT
336             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
337 #endif /* FX_ENABLE_FAULT_TOLERANT */
338 
339             /* Release media protection.  */
340             FX_UNPROTECT
341 
342             /* This is an error that suggests a corrupt file.  */
343             return(FX_FILE_CORRUPT);
344         }
345 
346         /* Position the pointers to the new offset.  */
347         file_ptr -> fx_file_current_physical_cluster =  last_cluster;
348         file_ptr -> fx_file_current_relative_cluster =  cluster_count - 1;
349         file_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
350             (((ULONG64)file_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
351              ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
352             (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector);
353         file_ptr -> fx_file_current_relative_sector =   (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
354         file_ptr -> fx_file_current_file_offset =       size;
355         file_ptr -> fx_file_current_logical_offset =    (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector);
356     }
357 
358     /* Determine how many clusters are actually in-use now.  */
359     cluster_count =  (ULONG) (file_ptr -> fx_file_current_available_size + (((ULONG64) bytes_per_cluster) - ((ULONG64) 1)))/bytes_per_cluster;
360 
361     /* Save the number of clusters in-use.  */
362     file_ptr -> fx_file_total_clusters =  cluster_count;
363 
364     /* At this point, we are ready to walk list of clusters to find the clusters
365        that can be released.  */
366     cluster =           file_ptr -> fx_file_first_physical_cluster;
367 
368 #ifdef FX_ENABLE_FAULT_TOLERANT
369     /* Is this the last used cluster?  */
370     if ((cluster_count == 0) && (media_ptr -> fx_media_fault_tolerant_enabled))
371     {
372 
373         /* Set undo log. */
374         status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, FX_FREE_CLUSTER,
375                                                   media_ptr -> fx_media_fat_last, cluster, media_ptr -> fx_media_fat_last);
376 
377         /* Determine if the write was successful.  */
378         if (status != FX_SUCCESS)
379         {
380 
381             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
382 
383             /* Release media protection.  */
384             FX_UNPROTECT
385 
386             /* Return the error code.  */
387             return(status);
388         }
389 
390         file_ptr -> fx_file_last_physical_cluster =  cluster;
391     }
392 #endif /* FX_ENABLE_FAULT_TOLERANT */
393 
394     /* Follow the link of FAT entries.  */
395     while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
396     {
397 
398         /* Read the current cluster entry from the FAT.  */
399         status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
400 
401         /* Check the return value.  */
402         if (status != FX_SUCCESS)
403         {
404 
405 #ifdef FX_ENABLE_FAULT_TOLERANT
406             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
407 #endif /* FX_ENABLE_FAULT_TOLERANT */
408 
409             /* Release media protection.  */
410             FX_UNPROTECT
411 
412             /* Return the error status.  */
413             return(status);
414         }
415 
416         /* Determine if are more clusters to release.  */
417         if (cluster_count > 0)
418         {
419 
420             /* Decrement the number of clusters.  */
421             cluster_count--;
422 
423             /* Is this the last used cluster?  */
424             if (cluster_count == 0)
425             {
426                 {
427 
428 #ifdef FX_ENABLE_FAULT_TOLERANT
429                     if (media_ptr -> fx_media_fault_tolerant_enabled)
430                     {
431 
432                         /* Set undo phase. */
433                         media_ptr -> fx_media_fault_tolerant_state |= FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN;
434 
435                         /* Read the current cluster entry from the FAT.  */
436                         status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
437 
438                         /* Check the return value.  */
439                         if (status != FX_SUCCESS)
440                         {
441 
442                             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
443 
444                             /* Release media protection.  */
445                             FX_UNPROTECT
446 
447                             /* Return the error status.  */
448                             return(status);
449                         }
450 
451                         /* Set undo log. */
452                         status = _fx_fault_tolerant_set_FAT_chain(media_ptr, FX_FALSE, cluster,
453                                                                   media_ptr -> fx_media_fat_last, contents, media_ptr -> fx_media_fat_last);
454 
455                         /* Determine if the write was successful.  */
456                         if (status != FX_SUCCESS)
457                         {
458 
459                             FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
460 
461                             /* Release media protection.  */
462                             FX_UNPROTECT
463 
464                             /* Return the error code.  */
465                             return(status);
466                         }
467                     }
468 #endif /* FX_ENABLE_FAULT_TOLERANT */
469 
470                     /* Yes, it should be designated as last cluster.  */
471                     status = _fx_utility_FAT_entry_write(media_ptr, cluster, media_ptr -> fx_media_fat_last);
472 
473                     /* Check the return value.  */
474                     if (status)
475                     {
476 
477 #ifdef FX_ENABLE_FAULT_TOLERANT
478                         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
479 #endif /* FX_ENABLE_FAULT_TOLERANT */
480 
481                         /* Release media protection.  */
482                         FX_UNPROTECT
483 
484                         /* Return the error status.  */
485                         return(status);
486                     }
487 
488 #ifdef FX_ENABLE_FAULT_TOLERANT
489                     if (media_ptr -> fx_media_fault_tolerant_enabled)
490                     {
491 
492                         /* Clear undo phase. */
493                         media_ptr -> fx_media_fault_tolerant_state &= (UCHAR)(~FX_FAULT_TOLERANT_STATE_SET_FAT_CHAIN & 0xff);
494                     }
495 #endif /* FX_ENABLE_FAULT_TOLERANT */
496                 }
497 
498                 file_ptr -> fx_file_last_physical_cluster =  cluster;
499 
500 #ifdef FX_FAULT_TOLERANT
501 
502                 /* Flush the cached individual FAT entries.  */
503                 status = _fx_utility_FAT_flush(media_ptr);
504 
505                 /* Check the return value.  */
506                 if (status)
507                 {
508 
509 #ifdef FX_ENABLE_FAULT_TOLERANT
510                     FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
511 #endif /* FX_ENABLE_FAULT_TOLERANT */
512 
513                     /* Release media protection.  */
514                     FX_UNPROTECT
515 
516                     /* Return the error status.  */
517                     return(status);
518                 }
519 #endif
520             }
521         }
522         else
523         {
524 
525             /* This is a cluster after the clusters used by the file, release
526                it back to the media.  */
527 
528 #ifdef FX_ENABLE_FAULT_TOLERANT
529             if (media_ptr -> fx_media_fault_tolerant_enabled == FX_FALSE)
530             {
531 #endif /* FX_ENABLE_FAULT_TOLERANT */
532 
533                 status = _fx_utility_FAT_entry_write(media_ptr, cluster, FX_FREE_CLUSTER);
534 
535                 /* Check the return value.  */
536                 if (status)
537                 {
538 
539 #ifdef FX_ENABLE_FAULT_TOLERANT
540                     FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
541 #endif /* FX_ENABLE_FAULT_TOLERANT */
542 
543                     /* Release media protection.  */
544                     FX_UNPROTECT
545 
546                     /* Return the error status.  */
547                     return(status);
548                 }
549 
550                 /* Increment the number of available clusters.  */
551                 media_ptr -> fx_media_available_clusters++;
552 
553 #ifdef FX_ENABLE_FAULT_TOLERANT
554             }
555 #endif /* FX_ENABLE_FAULT_TOLERANT */
556         }
557 
558         /* Setup for the next cluster.  */
559         cluster =  contents;
560     }
561 
562     /* Determine if we need to adjust the number of leading consecutive clusters.  */
563     if (file_ptr -> fx_file_consecutive_cluster > file_ptr -> fx_file_total_clusters)
564     {
565 
566         /* Adjust the leading consecutive cluster count. */
567         file_ptr -> fx_file_consecutive_cluster =  file_ptr -> fx_file_total_clusters;
568     }
569 
570     /* Determine if the file available size has been truncated to zero.  */
571     if (file_ptr -> fx_file_current_available_size == 0)
572     {
573 
574         /* Yes, the first cluster has already been released.  Update the file info
575            to indicate the file has no clusters.  */
576         file_ptr -> fx_file_last_physical_cluster =     0;
577         file_ptr -> fx_file_first_physical_cluster =    0;
578         file_ptr -> fx_file_current_physical_cluster =  0;
579         file_ptr -> fx_file_current_logical_sector =    0;
580         file_ptr -> fx_file_current_relative_cluster =  0;
581         file_ptr -> fx_file_current_relative_sector =   0;
582         file_ptr -> fx_file_current_available_size =    0;
583         file_ptr -> fx_file_consecutive_cluster =       1;
584     }
585 
586 #ifndef FX_DONT_UPDATE_OPEN_FILES
587 
588     /* Search the opened files list to see if the same file is opened for reading.  */
589     open_count =  media_ptr -> fx_media_opened_file_count;
590     search_ptr =  media_ptr -> fx_media_opened_file_list;
591     while (open_count)
592     {
593 
594         /* Is this file the same file opened for reading?  */
595         if ((search_ptr != file_ptr) &&
596             (search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector ==
597              file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector) &&
598             (search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset ==
599              file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset))
600         {
601 
602             /* Yes, the same file is opened for reading.  */
603 
604             /* Setup the new file size.  */
605             search_ptr -> fx_file_current_file_size =  size;
606 
607             /* Setup the new total clusters.  */
608             search_ptr -> fx_file_total_clusters =  file_ptr -> fx_file_total_clusters;
609 
610             /* Copy the directory entry.  */
611             search_ptr -> fx_file_dir_entry.fx_dir_entry_cluster =      file_ptr -> fx_file_dir_entry.fx_dir_entry_cluster;
612             search_ptr -> fx_file_dir_entry.fx_dir_entry_file_size =    file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size;
613             search_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector =   file_ptr -> fx_file_dir_entry.fx_dir_entry_log_sector;
614             search_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset =  file_ptr -> fx_file_dir_entry.fx_dir_entry_byte_offset;
615 
616             /* Setup the other file parameters.  */
617             search_ptr -> fx_file_last_physical_cluster =     file_ptr -> fx_file_last_physical_cluster;
618             search_ptr -> fx_file_first_physical_cluster =    file_ptr -> fx_file_first_physical_cluster;
619             search_ptr -> fx_file_current_available_size =    file_ptr -> fx_file_current_available_size;
620             search_ptr -> fx_file_consecutive_cluster =       file_ptr -> fx_file_consecutive_cluster;
621 
622             /* Determine if the truncated file is smaller than the current file offset.  */
623             if (search_ptr -> fx_file_current_file_offset > size)
624             {
625 
626                 /* Yes, the current file parameters need to be changed since the file was
627                    truncated to a position prior to the current file position.  */
628 
629                 /* Calculate the number of bytes per cluster.  */
630                 bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
631                     ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
632 
633                 /* At this point, we are ready to walk list of clusters to setup the
634                    seek position of this file.  */
635                 cluster =           search_ptr -> fx_file_first_physical_cluster;
636                 bytes_remaining =   size;
637                 last_cluster =      0;
638                 cluster_count =     0;
639 
640                 /* Follow the link of FAT entries.  */
641                 while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
642                 {
643 
644                     /* Increment the number of clusters.  */
645                     cluster_count++;
646 
647 
648                     /* Read the current cluster entry from the FAT.  */
649                     status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &contents);
650 
651                     /* Check the return value.  */
652                     if (status != FX_SUCCESS)
653                     {
654 
655 #ifdef FX_ENABLE_FAULT_TOLERANT
656                         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
657 #endif /* FX_ENABLE_FAULT_TOLERANT */
658 
659                         /* Release media protection.  */
660                         FX_UNPROTECT
661 
662                         /* Return the error status.  */
663                         return(status);
664                     }
665 
666                     /* Save the last valid cluster.  */
667                     last_cluster =  cluster;
668 
669                     /* Setup for the next cluster.  */
670                     cluster =  contents;
671 
672                     /* Determine if this is the last written cluster.  */
673                     if (bytes_remaining >= bytes_per_cluster)
674                     {
675 
676                         /* Still more seeking, just decrement the working byte offset.  */
677                         bytes_remaining =  bytes_remaining - bytes_per_cluster;
678                     }
679                     else
680                     {
681 
682                         /* This is the cluster that contains the seek position.  */
683                         break;
684                     }
685                 }
686 
687                 /* Check for errors in traversal of the FAT chain.  */
688                 if (size > (((ULONG64) bytes_per_cluster) * ((ULONG64) cluster_count)))
689                 {
690 
691 #ifdef FX_ENABLE_FAULT_TOLERANT
692                     FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
693 #endif /* FX_ENABLE_FAULT_TOLERANT */
694 
695                     /* Release media protection.  */
696                     FX_UNPROTECT
697 
698                     /* This is an error that suggests a corrupt file.  */
699                     return(FX_FILE_CORRUPT);
700                 }
701 
702                 /* Position the pointers to the new offset.  */
703 
704                 /* Determine if there is at least one cluster.  */
705                 if (cluster_count)
706                 {
707 
708                     /* Calculate real file parameters.  */
709                     search_ptr -> fx_file_current_physical_cluster =  last_cluster;
710                     search_ptr -> fx_file_current_relative_cluster =  cluster_count - 1;
711                     search_ptr -> fx_file_current_logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
712                         (((ULONG64)search_ptr -> fx_file_current_physical_cluster - FX_FAT_ENTRY_START) *
713                          ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
714                         (bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector);
715                     search_ptr -> fx_file_current_relative_sector =   (UINT)((bytes_remaining / (ULONG)media_ptr -> fx_media_bytes_per_sector));
716                     search_ptr -> fx_file_current_file_offset =       size;
717                     search_ptr -> fx_file_current_logical_offset =    (ULONG)bytes_remaining % ((ULONG)media_ptr -> fx_media_bytes_per_sector);
718                 }
719                 else
720                 {
721 
722                     /* Calculate zero-length file parameters.  */
723                     search_ptr -> fx_file_current_physical_cluster =  0;
724                     search_ptr -> fx_file_current_relative_cluster =  0;
725                     search_ptr -> fx_file_current_logical_sector =    0;
726                     search_ptr -> fx_file_current_relative_sector =   0;
727                     search_ptr -> fx_file_current_file_offset =       0;
728                     search_ptr -> fx_file_current_logical_offset =    0;
729                 }
730             }
731         }
732 
733         /* Adjust the pointer and decrement the search count.  */
734         search_ptr =  search_ptr -> fx_file_opened_next;
735         open_count--;
736     }
737 #endif
738 
739 #ifdef FX_FAULT_TOLERANT
740 
741     /* Flush the cached individual FAT entries */
742     status = _fx_utility_FAT_flush(media_ptr);
743 
744     /* Check the return value.  */
745     if (status)
746     {
747 
748 #ifdef FX_ENABLE_FAULT_TOLERANT
749         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
750 #endif /* FX_ENABLE_FAULT_TOLERANT */
751 
752         /* Release media protection.  */
753         FX_UNPROTECT
754 
755         /* Return the error status.  */
756         return(status);
757     }
758 
759 #endif
760 
761 #ifdef FX_ENABLE_FAULT_TOLERANT
762     if (media_ptr -> fx_media_fault_tolerant_enabled &&
763         (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED))
764     {
765 
766         /* Copy the new file size into the directory entry.  */
767         file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size = file_ptr -> fx_file_current_file_size;
768     }
769 
770     /* Write the directory entry to the media.  */
771     status =  _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry));
772 
773     /* Check for a good status.  */
774     if (status != FX_SUCCESS)
775     {
776 
777         FX_FAULT_TOLERANT_TRANSACTION_FAIL(media_ptr);
778 
779         /* Release media protection.  */
780         FX_UNPROTECT
781 
782         /* Error writing the directory.  */
783         return(status);
784     }
785 
786     /* End transaction. */
787     status = _fx_fault_tolerant_transaction_end(media_ptr);
788 
789     /* Check for a bad status.  */
790     if (status != FX_SUCCESS)
791     {
792 
793         /* Release media protection.  */
794         FX_UNPROTECT
795 
796         /* Return the bad status.  */
797         return(status);
798     }
799 
800     /* Update maximum size used if necessary. */
801     if (size < file_ptr -> fx_file_maximum_size_used)
802     {
803         file_ptr -> fx_file_maximum_size_used = size;
804     }
805 #endif /* FX_ENABLE_FAULT_TOLERANT */
806 
807     /* Release media protection.  */
808     FX_UNPROTECT
809 
810     /* Truncate is complete, return successful status.  */
811     return(FX_SUCCESS);
812 }
813 
814