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