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 /**   Media                                                               */
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_media.h"
30 #include "fx_file.h"
31 #include "fx_directory.h"
32 #include "fx_utility.h"
33 
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _fx_media_close                                     PORTABLE C      */
40 /*                                                           6.1          */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    William E. Lamie, Microsoft Corporation                             */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function examines the list of open files for this media and    */
48 /*    closes each file.  If a file has been written to, the file's        */
49 /*    directory information is also written out to the media.  After      */
50 /*    the files have been closed, the internal logical sector is          */
51 /*    flushed and a flush command is sent to the attached driver.         */
52 /*    Finally, this media control block is removed from the list of       */
53 /*    opened media control blocks and is marked as closed.                */
54 /*                                                                        */
55 /*  INPUT                                                                 */
56 /*                                                                        */
57 /*    media_ptr                             Media control block pointer   */
58 /*                                                                        */
59 /*  OUTPUT                                                                */
60 /*                                                                        */
61 /*    return status                                                       */
62 /*                                                                        */
63 /*  CALLS                                                                 */
64 /*                                                                        */
65 /*    _fx_directory_entry_write             Write the directory entry     */
66 /*    _fx_media_abort                       Abort the media on error      */
67 /*    _fx_utility_FAT_flush                 Flush cached FAT entries      */
68 /*    _fx_utility_FAT_map_flush             Flush primary FAT changes to  */
69 /*                                            secondary FAT(s)            */
70 /*    _fx_utility_logical_sector_flush      Flush logical sector cache    */
71 /*    _fx_utility_16_unsigned_read          Read a 16-bit value           */
72 /*    _fx_utility_32_unsigned_read          Read a 32-bit value           */
73 /*    _fx_utility_32_unsigned_write         Write a 32-bit value          */
74 /*    tx_mutex_delete                       Delete protection mutex       */
75 /*                                                                        */
76 /*  CALLED BY                                                             */
77 /*                                                                        */
78 /*    Application Code                                                    */
79 /*                                                                        */
80 /*  RELEASE HISTORY                                                       */
81 /*                                                                        */
82 /*    DATE              NAME                      DESCRIPTION             */
83 /*                                                                        */
84 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
85 /*  09-30-2020     William E. Lamie         Modified comment(s), and      */
86 /*                                            added conditional to        */
87 /*                                            disable file close          */
88 /*                                            and cache,                  */
89 /*                                            resulting in version 6.1    */
90 /*                                                                        */
91 /**************************************************************************/
_fx_media_close(FX_MEDIA * media_ptr)92 UINT  _fx_media_close(FX_MEDIA  *media_ptr)
93 {
94 
95 FX_INT_SAVE_AREA
96 
97 #ifndef FX_DISABLE_FILE_CLOSE
98 ULONG    open_count;
99 FX_FILE *file_ptr;
100 #endif /* FX_DISABLE_FILE_CLOSE */
101 UINT     status;
102 
103 
104     /* Check the media to make sure it is open.  */
105     if (media_ptr -> fx_media_id != FX_MEDIA_ID)
106     {
107 
108         /* Return the media not opened error.  */
109         return(FX_MEDIA_NOT_OPEN);
110     }
111 
112     /* If trace is enabled, insert this event into the trace buffer.  */
113     FX_TRACE_IN_LINE_INSERT(FX_TRACE_MEDIA_CLOSE, media_ptr, 0, 0, 0, FX_TRACE_MEDIA_EVENTS, 0, 0)
114 
115     /* If trace is enabled, unregister this object.  */
116     FX_TRACE_OBJECT_UNREGISTER(media_ptr)
117 
118     /* Protect against other threads accessing the media.  */
119     FX_PROTECT
120 
121 #ifndef FX_DISABLE_FILE_CLOSE
122     /* Loop through the media's open files.  */
123     open_count =  media_ptr -> fx_media_opened_file_count;
124     file_ptr =    media_ptr -> fx_media_opened_file_list;
125     while (open_count)
126     {
127 
128         /* Look at each opened file to see if the same file is opened
129            for writing and has been written to.  */
130         if ((file_ptr -> fx_file_open_mode == FX_OPEN_FOR_WRITE) &&
131             (file_ptr -> fx_file_modified))
132         {
133 
134             /* Lockout interrupts for time/date access.  */
135             FX_DISABLE_INTS
136 
137             /* Set the new time and date.  */
138             file_ptr -> fx_file_dir_entry.fx_dir_entry_time =  _fx_system_time;
139             file_ptr -> fx_file_dir_entry.fx_dir_entry_date =  _fx_system_date;
140 
141             /* Restore interrupt posture.  */
142             FX_RESTORE_INTS
143 
144             /* Copy the new file size into the directory entry.  */
145             file_ptr -> fx_file_dir_entry.fx_dir_entry_file_size =
146                 file_ptr -> fx_file_current_file_size;
147 
148             /* Write the directory entry to the media.  */
149             status = _fx_directory_entry_write(media_ptr, &(file_ptr -> fx_file_dir_entry));
150 
151             /* Determine if the status was unsuccessful. */
152             if (status != FX_SUCCESS)
153             {
154 
155                 /* Release media protection.  */
156                 FX_UNPROTECT
157 
158                 /* Call the media abort routine.  */
159                 _fx_media_abort(media_ptr);
160 
161                 /* Return the error status.  */
162                 return(FX_IO_ERROR);
163             }
164 
165             /* Clear the file modified flag.  */
166             file_ptr -> fx_file_modified =  FX_FALSE;
167         }
168 
169         /* Mark the file as closed.  */
170         file_ptr -> fx_file_id =  FX_FILE_CLOSED_ID;
171 
172         /* Adjust the pointer and decrement the opened count.  */
173         file_ptr =  file_ptr -> fx_file_opened_next;
174         open_count--;
175     }
176 #endif /* FX_DISABLE_FILE_CLOSE */
177 
178     /* Flush the cached individual FAT entries */
179     _fx_utility_FAT_flush(media_ptr);
180 
181     /* Flush changed sector(s) in the primary FAT to secondary FATs.  */
182     _fx_utility_FAT_map_flush(media_ptr);
183 
184 
185     /* Flush the internal logical sector cache.  */
186     status =  _fx_utility_logical_sector_flush(media_ptr, ((ULONG64) 1), (ULONG64) (media_ptr -> fx_media_total_sectors), FX_FALSE);
187 
188     /* Determine if the flush was unsuccessful. */
189     if (status != FX_SUCCESS)
190     {
191 
192         /* Release media protection.  */
193         FX_UNPROTECT
194 
195         /* Call the media abort routine.  */
196         _fx_media_abort(media_ptr);
197 
198         /* Return the error status.  */
199         return(FX_IO_ERROR);
200     }
201 
202     /* Determine if the media needs to have the additional information sector updated. This will
203        only be the case for 32-bit FATs. The logic here only needs to be done if the last reported
204        available cluster count is different that the currently available clusters.  */
205     if ((media_ptr -> fx_media_FAT32_additional_info_sector) &&
206         (media_ptr -> fx_media_FAT32_additional_info_last_available != media_ptr -> fx_media_available_clusters) &&
207         (media_ptr -> fx_media_driver_write_protect == FX_FALSE))
208     {
209 
210     UCHAR *buffer_ptr;
211     ULONG  signature;
212 
213 
214 #ifndef FX_DISABLE_CACHE
215         /* Setup a pointer to the first cached entry's buffer.  */
216         buffer_ptr =  (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_memory_buffer;
217 
218         /* Invalidate this cache entry.  */
219         (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector =  (~(ULONG64)0);
220         (media_ptr -> fx_media_sector_cache_list_ptr) -> fx_cached_sector_valid =  FX_FALSE;
221 #else
222         buffer_ptr =  media_ptr -> fx_media_memory_buffer;
223 #endif /* FX_DISABLE_CACHE */
224 
225         /* Read the FAT32 additional information sector from the device.  */
226         media_ptr -> fx_media_driver_request =          FX_DRIVER_READ;
227         media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
228         media_ptr -> fx_media_driver_buffer =           buffer_ptr;
229         media_ptr -> fx_media_driver_logical_sector =   media_ptr -> fx_media_FAT32_additional_info_sector;
230         media_ptr -> fx_media_driver_sectors =          1;
231         media_ptr -> fx_media_driver_sector_type =      FX_DIRECTORY_SECTOR;
232 
233 #ifndef FX_MEDIA_STATISTICS_DISABLE
234 
235         /* Increment the number of driver read sector(s) requests.  */
236         media_ptr -> fx_media_driver_read_requests++;
237 #endif
238 
239         /* If trace is enabled, insert this event into the trace buffer.  */
240         FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_READ, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
241 
242         /* Invoke the driver to read the FAT32 additional information sector.  */
243         (media_ptr -> fx_media_driver_entry) (media_ptr);
244 
245         /* Determine if the FAT32 sector was read correctly. */
246         if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
247         {
248 
249             /* Release media protection.  */
250             FX_UNPROTECT
251 
252             /* Call the media abort routine.  */
253             _fx_media_abort(media_ptr);
254 
255             /* Return the error status.  */
256             return(FX_IO_ERROR);
257         }
258 
259         /* Setup a pointer into the FAT32 additional information sector.  */
260         buffer_ptr =  media_ptr -> fx_media_driver_buffer;
261 
262         /* Pickup the first signature long word.  */
263         signature =  _fx_utility_32_unsigned_read(&buffer_ptr[0]);
264 
265         /* Determine if the signature is correct.  */
266         if (signature == 0x41615252)
267         {
268 
269             /* Yes, the first signature is correct, now pickup the next signature.  */
270             signature =  _fx_utility_32_unsigned_read(&buffer_ptr[484]);
271 
272             /* Determine if this signature is correct.  */
273             if (signature == 0x61417272)
274             {
275 
276                 /* Yes, we have a good FAT32 additional information sector.  */
277 
278                 /* Set the free cluster count to the available clusters in the media control block.  */
279                 _fx_utility_32_unsigned_write(&buffer_ptr[488], media_ptr -> fx_media_available_clusters);
280 
281                 /* Set the next free cluster number hint to starting search cluster in the media control block.  */
282                 _fx_utility_32_unsigned_write(&buffer_ptr[492], media_ptr -> fx_media_cluster_search_start);
283 
284                 /* Now write the sector back out to the media.  */
285                 media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
286                 media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
287                 media_ptr -> fx_media_driver_buffer =           buffer_ptr;
288                 media_ptr -> fx_media_driver_logical_sector =   media_ptr -> fx_media_FAT32_additional_info_sector;
289                 media_ptr -> fx_media_driver_sectors =          1;
290                 media_ptr -> fx_media_driver_sector_type =      FX_DIRECTORY_SECTOR;
291 
292                 /* Set the system write flag since we are writing a directory sector.  */
293                 media_ptr -> fx_media_driver_system_write =  FX_TRUE;
294 
295 #ifndef FX_MEDIA_STATISTICS_DISABLE
296 
297                 /* Increment the number of driver write sector(s) requests.  */
298                 media_ptr -> fx_media_driver_write_requests++;
299 #endif
300 
301                 /* If trace is enabled, insert this event into the trace buffer.  */
302                 FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, media_ptr -> fx_media_FAT32_additional_info_sector, 1, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
303 
304                 /* Invoke the driver to write the FAT32 additional information sector.  */
305                 (media_ptr -> fx_media_driver_entry) (media_ptr);
306 
307                 /* Clear the system write flag.  */
308                 media_ptr -> fx_media_driver_system_write =  FX_FALSE;
309 
310                 /* Determine if the FAT32 sector was written correctly. */
311                 if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
312                 {
313 
314                     /* Release media protection.  */
315                     FX_UNPROTECT
316 
317                     /* Call the media abort routine.  */
318                     _fx_media_abort(media_ptr);
319 
320                     /* Return the sector IO error status.  */
321                     return(FX_IO_ERROR);
322                 }
323 
324                 /* Successful update of the FAT32 additional information sector. Update the
325                    last written available cluster count.  */
326                 media_ptr -> fx_media_FAT32_additional_info_last_available =  media_ptr -> fx_media_available_clusters;
327             }
328         }
329     }
330 
331 #ifndef FX_MEDIA_STATISTICS_DISABLE
332 
333     /* Increment the number of driver flush requests.  */
334     media_ptr -> fx_media_driver_flush_requests++;
335 #endif
336 
337     /* Build the "flush" I/O driver request.  */
338     media_ptr -> fx_media_driver_request =      FX_DRIVER_FLUSH;
339     media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
340 
341     /* If trace is enabled, insert this event into the trace buffer.  */
342     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_FLUSH, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
343 
344     /* Call the specified I/O driver with the flush request.  */
345     (media_ptr -> fx_media_driver_entry) (media_ptr);
346 
347     /* Build the "uninitialize" I/O driver request.  */
348     media_ptr -> fx_media_driver_request =      FX_DRIVER_UNINIT;
349     media_ptr -> fx_media_driver_status =       FX_IO_ERROR;
350 
351     /* If trace is enabled, insert this event into the trace buffer.  */
352     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_UNINIT, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
353 
354     /* Call the specified I/O driver with the uninitialize request.  */
355     (media_ptr -> fx_media_driver_entry) (media_ptr);
356 
357     /* Now remove this media from the open list.  */
358 
359     /* Lockout interrupts for media removal.  */
360     FX_DISABLE_INTS
361 
362     /* See if the media is the only one on the media opened list.  */
363     if (_fx_system_media_opened_count == ((ULONG) 1))
364     {
365 
366         /* Only opened media, just set the opened list to NULL.  */
367         _fx_system_media_opened_ptr =  FX_NULL;
368     }
369     else
370     {
371 
372         /* Otherwise, not the only opened media, link-up the neighbors.  */
373         (media_ptr -> fx_media_opened_next) -> fx_media_opened_previous =
374             media_ptr -> fx_media_opened_previous;
375         (media_ptr -> fx_media_opened_previous) -> fx_media_opened_next =
376             media_ptr -> fx_media_opened_next;
377 
378         /* See if we have to update the opened list head pointer.  */
379         if (_fx_system_media_opened_ptr == media_ptr)
380         {
381 
382             /* Yes, move the head pointer to the next opened media. */
383             _fx_system_media_opened_ptr =  media_ptr -> fx_media_opened_next;
384         }
385     }
386 
387     /* Decrement the opened media counter.  */
388     _fx_system_media_opened_count--;
389 
390     /* Finally, Indicate that this media is closed.  */
391     media_ptr -> fx_media_id =  FX_MEDIA_CLOSED_ID;
392 
393     /* Restore interrupt posture.  */
394     FX_RESTORE_INTS
395 
396     /* Delete the media protection structure if FX_SINGLE_THREAD is not
397        defined.  */
398 #ifndef FX_SINGLE_THREAD
399 
400 #ifndef FX_DONT_CREATE_MUTEX
401 
402     /* Note that the protection is never released. The mutex delete
403        service will handle all threads waiting access to this media
404        control block.  */
405     tx_mutex_delete(& (media_ptr -> fx_media_protect));
406 #endif
407 #endif
408 
409     /* Invoke media close callback. */
410     if (media_ptr -> fx_media_close_notify)
411     {
412         media_ptr -> fx_media_close_notify(media_ptr);
413     }
414 
415 #ifdef FX_DONT_CREATE_MUTEX
416 
417     /* Release media protection.  */
418     FX_UNPROTECT
419 #endif
420 
421     /* Return success status to the caller.  */
422     return(FX_SUCCESS);
423 }
424 
425