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 /**   Directory                                                           */
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_utility.h"
31 
32 
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _fx_directory_free_search                           PORTABLE C      */
38 /*                                                           6.1.12       */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function searches the media for a free directory entry.        */
46 /*                                                                        */
47 /*  INPUT                                                                 */
48 /*                                                                        */
49 /*    media_ptr                             Media control block pointer   */
50 /*    directory_ptr                         Pointer to directory to       */
51 /*                                            search in                   */
52 /*    entry_ptr                             Pointer to directory entry    */
53 /*                                            record                      */
54 /*                                                                        */
55 /*  OUTPUT                                                                */
56 /*                                                                        */
57 /*    return status                                                       */
58 /*                                                                        */
59 /*  CALLS                                                                 */
60 /*                                                                        */
61 /*    _fx_directory_entry_read              Read entries from directory   */
62 /*    _fx_directory_entry_write             Write entries to directory    */
63 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
64 /*    _fx_utility_FAT_entry_write           Write a FAT entry             */
65 /*    _fx_utility_FAT_flush                 Flush written FAT entries     */
66 /*    _fx_utility_logical_sector_flush      Flush logical sector cache    */
67 /*    _fx_utility_logical_sector_read       Read logical sector           */
68 /*    _fx_utility_logical_sector_write      Write logical sector          */
69 /*                                                                        */
70 /*  CALLED BY                                                             */
71 /*                                                                        */
72 /*    FileX System Functions                                              */
73 /*                                                                        */
74 /*  RELEASE HISTORY                                                       */
75 /*                                                                        */
76 /*    DATE              NAME                      DESCRIPTION             */
77 /*                                                                        */
78 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
79 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
80 /*                                            resulting in version 6.1    */
81 /*  07-29-2022     Bhupendra Naphade        Modified comment(s),          */
82 /*                                            updated available cluster   */
83 /*                                            check for sub directory,    */
84 /*                                            resulting in version 6.1.12 */
85 /*                                                                        */
86 /**************************************************************************/
_fx_directory_free_search(FX_MEDIA * media_ptr,FX_DIR_ENTRY * directory_ptr,FX_DIR_ENTRY * entry_ptr)87 UINT  _fx_directory_free_search(FX_MEDIA *media_ptr, FX_DIR_ENTRY *directory_ptr, FX_DIR_ENTRY *entry_ptr)
88 {
89 
90 ULONG         i, j;
91 UCHAR        *work_ptr;
92 UINT          status, total_entries;
93 ULONG         entry_sector, entry_offset;
94 ULONG         FAT_index, FAT_value;
95 ULONG         cluster, total_clusters, clusters_needed;
96 ULONG         first_new_cluster, last_cluster, clusters;
97 ULONG         directory_index;
98 ULONG         directory_entries;
99 ULONG         logical_sector;
100 FX_DIR_ENTRY *search_dir_ptr;
101 ULONG         free_entry_start;
102 UINT          sectors;
103 
104 FX_INT_SAVE_AREA
105 
106 
107 
108 #ifndef FX_MEDIA_STATISTICS_DISABLE
109 
110     /* Increment the number of directory free entry search requests.  */
111     media_ptr -> fx_media_directory_free_searches++;
112 #endif
113 
114     /* Initialize the entry sector values.  */
115     entry_sector = entry_offset = 0;
116 
117     /* Set the long file name flag to false.  */
118     entry_ptr -> fx_dir_entry_long_name_present =  0;
119 
120     /* Are there leading dots?  */
121     if (entry_ptr -> fx_dir_entry_name[0] == '.')
122     {
123 
124         /* Is there more than 1 dot?  */
125         if (entry_ptr -> fx_dir_entry_name[1] == '.')
126         {
127             /* Yes, consider the name invalid.  */
128             return(FX_INVALID_NAME);
129         }
130     }
131 
132     /* Determine if a long file name is present.  */
133     for (i = 0, j = 0; entry_ptr -> fx_dir_entry_name[i]; i++)
134     {
135 
136         /* Check for upper-case characters.  */
137         if ((entry_ptr -> fx_dir_entry_name[i] >= 'A') && (entry_ptr -> fx_dir_entry_name[i] <= 'Z'))
138         {
139             continue;
140         }
141         /* Check for numeric characters.  */
142         else if ((entry_ptr -> fx_dir_entry_name[i] >= '0') && (entry_ptr -> fx_dir_entry_name[i] <= '9'))
143         {
144             continue;
145         }
146         /* Check for any lower-case characters.  */
147         else if ((entry_ptr -> fx_dir_entry_name[i] >= 'a') && (entry_ptr -> fx_dir_entry_name[i] <= 'z'))
148         {
149             entry_ptr -> fx_dir_entry_long_name_present =  1;
150         }
151         /* Check for a space in the middle of the name.  */
152         else if (entry_ptr -> fx_dir_entry_name[i] == ' ')
153         {
154             entry_ptr -> fx_dir_entry_long_name_present = 1;
155         }
156         /* Check for a dot in the name.  */
157         else if (entry_ptr -> fx_dir_entry_name[i] == '.')
158         {
159             /* Determine if this is the first dot detected.  */
160             if (j == 0)
161             {
162                 /* First dot, remember where it was.  */
163                 j = i;
164 
165                 /* Determine if this is a leading dot.  */
166                 if (i == 0)
167                 {
168 
169                     /* Leading dot detected, treat as a long filename.  */
170                     entry_ptr -> fx_dir_entry_long_name_present =  1;
171                 }
172             }
173             else
174             {
175                 /* Second dot detected, must have a long file name.  */
176                 entry_ptr -> fx_dir_entry_long_name_present = 1;
177             }
178         }
179         /* Check for a special 0xE5 character.  */
180         else if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] == (UCHAR)0xE5)
181         {
182             entry_ptr -> fx_dir_entry_long_name_present = 1;
183         }
184         /* Check for code point value greater than 127.  */
185         else if ((UCHAR)entry_ptr -> fx_dir_entry_name[i] > (UCHAR)127)
186         {
187             continue;
188         }
189         /* Check for any special characters.  */
190         else if ((entry_ptr -> fx_dir_entry_name[i] == '~') ||
191                  (entry_ptr -> fx_dir_entry_name[i] == '-') ||
192                  (entry_ptr -> fx_dir_entry_name[i] == '_') ||
193                  (entry_ptr -> fx_dir_entry_name[i] == '}') ||
194                  (entry_ptr -> fx_dir_entry_name[i] == '{') ||
195                  (entry_ptr -> fx_dir_entry_name[i] == '(') ||
196                  (entry_ptr -> fx_dir_entry_name[i] == ')') ||
197                  (entry_ptr -> fx_dir_entry_name[i] == '`') ||
198                  (entry_ptr -> fx_dir_entry_name[i] == '\'') ||
199                  (entry_ptr -> fx_dir_entry_name[i] == '!') ||
200                  (entry_ptr -> fx_dir_entry_name[i] == '#') ||
201                  (entry_ptr -> fx_dir_entry_name[i] == '$') ||
202                  (entry_ptr -> fx_dir_entry_name[i] == '&') ||
203                  (entry_ptr -> fx_dir_entry_name[i] == '@') ||
204                  (entry_ptr -> fx_dir_entry_name[i] == '^') ||
205                  (entry_ptr -> fx_dir_entry_name[i] == '%'))
206         {
207             continue;
208         }
209         /* Check for long filename special characters.  */
210         else if ((entry_ptr -> fx_dir_entry_name[i] == '+') ||
211                  (entry_ptr -> fx_dir_entry_name[i] == ',') ||
212                  (entry_ptr -> fx_dir_entry_name[i] == ';') ||
213                  (entry_ptr -> fx_dir_entry_name[i] == '=') ||
214                  (entry_ptr -> fx_dir_entry_name[i] == '[') ||
215                  (entry_ptr -> fx_dir_entry_name[i] == ']'))
216         {
217             entry_ptr -> fx_dir_entry_long_name_present = 1;
218         }
219         /* Something is wrong with the supplied name.  */
220         else
221         {
222             return(FX_INVALID_NAME);
223         }
224     }
225 
226     /* Determine if a dot was found.  */
227     if (j != 0)
228     {
229 
230         /* Yes, Determine if the extension exceeds a 3 character extension.  */
231         if ((i - j) > 4)
232         {
233 
234             /* Yes, long file name is present.  */
235             entry_ptr -> fx_dir_entry_long_name_present = 1;
236         }
237     }
238 
239     /* Calculate the total entries needed.  */
240     if ((i <= 12) && (entry_ptr -> fx_dir_entry_long_name_present == 0))
241     {
242 
243         /* Initialize the total entries to 1.  */
244         total_entries = 1;
245 
246         /* Check for special instance of long file name.  */
247         if ((j >= 9) || ((i - j) >= 9))
248         {
249 
250             /* The dot is after 8 character or there is no dot and the name
251                is greater than 8 character. */
252             entry_ptr -> fx_dir_entry_long_name_present = 1;
253             total_entries = 2;
254         }
255     }
256     else
257     {
258 
259         /* Long file name is present, calculate how many entries are needed
260            to represent it.  */
261         if (i % 13 == 0)
262         {
263             /* Exact fit, just add one for the 8.3 short name.  */
264             total_entries = i / 13 + 1;
265         }
266         else
267         {
268             /* Non-exact fit, add two for 8.3 short name and overlap.  */
269             total_entries = i / 13 + 2;
270         }
271     }
272 
273     /* Determine if the search is in the root directory or in a
274        sub-directory.  Note: the directory search function clears the
275        first character of the name for the root directory.  */
276     if (directory_ptr -> fx_dir_entry_name[0])
277     {
278 
279         /* Search for a free entry in a sub-directory.  */
280 
281         /* Pickup the number of entries in this directory.  This was placed
282            into the unused file size field.  */
283         directory_entries =  (ULONG)directory_ptr -> fx_dir_entry_file_size;
284 
285         /* Point the search directory pointer to this entry.  */
286         search_dir_ptr =  directory_ptr;
287 
288         /* Ensure that the search directory's last search cluster is cleared.  */
289         search_dir_ptr -> fx_dir_entry_last_search_cluster =  0;
290 
291         /* Set the initial index to 2, since the first two directory entries are
292            always allocated.  */
293         directory_index =  2;
294     }
295     else
296     {
297 
298         /* Find a free entry in the root directory.  */
299 
300         /* Setup the number of directory entries.  */
301         directory_entries =  (ULONG)media_ptr -> fx_media_root_directory_entries;
302 
303         /* Set the search pointer to NULL since we are working off of the
304            root directory.  */
305         search_dir_ptr =  FX_NULL;
306 
307         /* Set the initial index to 0, since the first entry of the root directory is valid.  */
308         directory_index =  0;
309     }
310 
311     /* Loop through entries in the search directory.  Yes, this is a
312        linear search!  */
313     free_entry_start = directory_entries;
314     do
315     {
316 
317         /* Read an entry from the directory.  */
318         status =  _fx_directory_entry_read(media_ptr, search_dir_ptr, &directory_index, entry_ptr);
319 
320         /* Check for error status.  */
321         if (status != FX_SUCCESS)
322         {
323             return(status);
324         }
325 
326         /* Determine if this is an empty entry.  */
327         if ((((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0)) ||
328             ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_DONE))
329         {
330 
331             /* Determine how many entries are needed.  */
332             if (total_entries > 1)
333             {
334 
335                 /* Multiple entries are needed for long file names.  Mark this
336                    entry as free. */
337                 if (entry_ptr -> fx_dir_entry_name[0] == FX_DIR_ENTRY_DONE)
338                 {
339 
340                     entry_ptr -> fx_dir_entry_long_name_present =  0;
341                     entry_ptr -> fx_dir_entry_name[0] =      (CHAR)FX_DIR_ENTRY_FREE;
342                     entry_ptr -> fx_dir_entry_name[1] =      (CHAR)0;
343 
344                     /* Write out the directory entry.  */
345                     status = _fx_directory_entry_write(media_ptr, entry_ptr);
346                     if(status != FX_SUCCESS)
347                     {
348                         return(status);
349                     }
350 
351                     /* Note that for long names we need to avoid holes in the middle,
352                        i.e. entries must be logically contiguous.  */
353                 }
354             }
355 
356             /* Determine if we are at the first free entry.  */
357             if (free_entry_start == directory_entries)
358             {
359 
360                 /* Remember the start of the free entry.  */
361                 free_entry_start =  directory_index;
362                 entry_sector =      (ULONG)entry_ptr -> fx_dir_entry_log_sector;
363                 entry_offset  =     entry_ptr -> fx_dir_entry_byte_offset;
364             }
365 
366             /* Determine if there are enough free entries to satisfy the request.  */
367             if ((directory_index - free_entry_start + 1) >= total_entries)
368             {
369 
370                 /* Found an empty slot.  Most pertinent information is already
371                    in the entry structure.  */
372 
373                 /* Setup the the sector and the offset.  */
374                 entry_ptr -> fx_dir_entry_log_sector =      entry_sector;
375                 entry_ptr -> fx_dir_entry_byte_offset =     entry_offset;
376 
377                 /* Initialize the additional directory entries.  */
378                 entry_ptr -> fx_dir_entry_reserved =            0;
379                 entry_ptr -> fx_dir_entry_created_time_ms =     0;
380 
381                 /* Lockout interrupts for time/date access.  */
382                 FX_DISABLE_INTS
383 
384                 entry_ptr -> fx_dir_entry_created_time =        _fx_system_time;
385                 entry_ptr -> fx_dir_entry_created_date =        _fx_system_date;
386                 entry_ptr -> fx_dir_entry_last_accessed_date =  _fx_system_date;
387 
388                 /* Restore interrupts.  */
389                 FX_RESTORE_INTS
390 
391                 /* Determine if a long file name is present.  */
392                 if (total_entries == 1)
393                 {
394                     entry_ptr -> fx_dir_entry_long_name_present =  0;
395                 }
396                 else
397                 {
398                     entry_ptr -> fx_dir_entry_long_name_present =  1;
399                 }
400 
401                 /* Return a successful completion.  */
402                 return(FX_SUCCESS);
403             }
404         }
405         else
406         {
407 
408             /* Reset the free entry start.  */
409             free_entry_start =  directory_entries;
410         }
411 
412         /* Move to the next entry.  */
413         directory_index++;
414 
415         /* Determine if we have exceeded the number of entries in the current directory.  */
416         if (directory_index >= directory_entries)
417         {
418 
419             /* Calculate how many sectors we need for the new directory entry.  */
420             sectors =  ((total_entries * FX_DIR_ENTRY_SIZE) + (media_ptr -> fx_media_bytes_per_sector - 1))/
421                                                                             media_ptr -> fx_media_bytes_per_sector;
422 
423             /* Now calculate how many clusters we need for the new directory entry.  */
424             clusters_needed = (sectors + (media_ptr -> fx_media_sectors_per_cluster - 1)) / media_ptr -> fx_media_sectors_per_cluster;
425 
426             /* Not enough empty entries were found.  If the specified directory is a sub-directory,
427                attempt to allocate another cluster to it.  */
428             if (((search_dir_ptr) || (media_ptr -> fx_media_32_bit_FAT)) && (media_ptr -> fx_media_available_clusters >= clusters_needed))
429             {
430 
431                 /* Search for the additional clusters we need.  */
432                 first_new_cluster =  0;
433                 total_clusters =     media_ptr -> fx_media_total_clusters;
434                 last_cluster =       0;
435                 FAT_index    =       media_ptr -> fx_media_cluster_search_start;
436                 clusters =           clusters_needed;
437 
438                 /* Loop to find the needed clusters.  */
439                 while (clusters)
440                 {
441 
442                     /* Decrease the cluster count.  */
443                     clusters--;
444 
445                     /* Loop to find the first available cluster.  */
446                     do
447                     {
448 
449                         /* Make sure we stop looking after one pass through the FAT table.  */
450                         if (!total_clusters)
451                         {
452 
453                             /* Something is wrong with the media - the desired clusters were
454                                not found in the FAT table.  */
455                             return(FX_NO_MORE_SPACE);
456                         }
457 
458                         /* Read FAT entry.  */
459                         status =  _fx_utility_FAT_entry_read(media_ptr, FAT_index, &FAT_value);
460 
461                         /* Check for a bad status.  */
462                         if (status != FX_SUCCESS)
463                         {
464 
465                             /* Return the bad status.  */
466                             return(status);
467                         }
468 
469                         /* Decrement the total cluster count.  */
470                         total_clusters--;
471 
472                         /* Determine if the FAT entry is free.  */
473                         if (FAT_value == FX_FREE_CLUSTER)
474                         {
475 
476                             /* Move cluster search pointer forward.  */
477                             media_ptr -> fx_media_cluster_search_start =  FAT_index + 1;
478 
479                             /* Determine if this needs to be wrapped.  */
480                             if (media_ptr -> fx_media_cluster_search_start >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START))
481                             {
482 
483                                 /* Wrap the search to the beginning FAT entry.  */
484                                 media_ptr -> fx_media_cluster_search_start =  FX_FAT_ENTRY_START;
485                             }
486 
487                             /* Break this loop.  */
488                             break;
489                         }
490                         else
491                         {
492 
493                             /* FAT entry is not free... Advance the FAT index.  */
494                             FAT_index++;
495 
496                             /* Determine if we need to wrap the FAT index around.  */
497                             if (FAT_index >= (media_ptr -> fx_media_total_clusters + FX_FAT_ENTRY_START))
498                             {
499 
500                                 /* Wrap the search to the beginning FAT entry.  */
501                                 FAT_index =  FX_FAT_ENTRY_START;
502                             }
503                         }
504                     } while (FX_TRUE);
505 
506                     /* We found an available cluster.  We now need to clear all of entries in
507                        each of the cluster's sectors.  */
508 
509                     /* Calculate the logical sector of this cluster.  */
510                     logical_sector =  ((ULONG) media_ptr -> fx_media_data_sector_start) +
511                                        ((((ULONG) FAT_index) - FX_FAT_ENTRY_START) *
512                                        ((ULONG) media_ptr -> fx_media_sectors_per_cluster));
513 
514                     /* Pickup the number of sectors for the next directory cluster.  */
515                     sectors =  media_ptr -> fx_media_sectors_per_cluster;
516 
517                     /* Read the logical sector just for cache reasons.  */
518                     status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector,
519                                                               media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
520 
521                     /* Check the return value.  */
522                     if (status != FX_SUCCESS)
523                     {
524 
525                         /* Return the error status.  */
526                         return(status);
527                     }
528 
529                     /* Clear the entire first sector of the new sub-directory cluster.  */
530                     work_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer;
531                     i =  0;
532                     while (i < media_ptr -> fx_media_bytes_per_sector)
533                     {
534 
535                         /* Clear 4 bytes.  */
536                         *((ULONG *)work_ptr) =  (ULONG)0;
537 
538                         /* Increment pointer.  */
539                         work_ptr =  work_ptr + sizeof(ULONG);
540 
541                         /* Increment counter.  */
542                         i =  i + (ULONG)sizeof(ULONG);
543                     }
544 
545                     /* Write the logical sector to ensure the zeros are written.  */
546                     status =  _fx_utility_logical_sector_write(media_ptr, (ULONG64) logical_sector,
547                                                                media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
548 
549                     /* Determine if the write was successful.  */
550                     if (status != FX_SUCCESS)
551                     {
552 
553                         /* Return the error code.  */
554                         return(status);
555                     }
556 
557                     /* Determine if there are more sectors to clear in the first cluster of the new
558                        sub-directory.  */
559                     if (sectors > 1)
560                     {
561 
562                         /* Yes, invalidate all cached sectors that are contained in the newly allocated first
563                            cluster of the directory.  */
564 
565                         /* Flush the internal logical sector cache.  */
566                         status =  _fx_utility_logical_sector_flush(media_ptr, (ULONG64) (logical_sector + 1), (ULONG64) (sectors - 1), FX_TRUE);
567 
568                         /* Determine if the flush was successful.  */
569                         if (status != FX_SUCCESS)
570                         {
571 
572                             /* Return the error code.  */
573                             return(status);
574                         }
575 
576                         /* Clear all additional sectors of new sub-directory.  */
577                         sectors--;
578                         while (sectors)
579                         {
580 
581 #ifndef FX_MEDIA_STATISTICS_DISABLE
582 
583                             /* Increment the number of driver write sector(s) requests.  */
584                             media_ptr -> fx_media_driver_write_requests++;
585 #endif
586 
587                             /* Build Write request to the driver.  */
588                             media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
589                             media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
590                             media_ptr -> fx_media_driver_buffer =           media_ptr -> fx_media_memory_buffer;
591                             media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector + ((ULONG)sectors);
592                             media_ptr -> fx_media_driver_sectors =          1;
593                             media_ptr -> fx_media_driver_sector_type =      FX_DIRECTORY_SECTOR;
594 
595                             /* Set the system write flag since we are writing a directory sector.  */
596                             media_ptr -> fx_media_driver_system_write =  FX_TRUE;
597 
598                             /* If trace is enabled, insert this event into the trace buffer.  */
599                             FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, ((ULONG)logical_sector) + ((ULONG)sectors), 1, media_ptr -> fx_media_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
600 
601                             /* Invoke the driver to write the sector.  */
602                             (media_ptr -> fx_media_driver_entry) (media_ptr);
603 
604                             /* Clear the system write flag.  */
605                             media_ptr -> fx_media_driver_system_write =  FX_FALSE;
606 
607                             /* Determine if an error occurred.  */
608                             if (media_ptr -> fx_media_driver_status != FX_SUCCESS)
609                             {
610 
611                                 /* Return error code.  */
612                                 return(media_ptr -> fx_media_driver_status);
613                             }
614 
615                             /* Decrease the number of sectors to clear.  */
616                             sectors--;
617                         }
618                     }
619 
620                     /* Determine if we have found the first new cluster yet.  */
621                     if (first_new_cluster == 0)
622                     {
623 
624                         /* Remember the first new cluster. */
625                         first_new_cluster =  FAT_index;
626                     }
627 
628                     /* Check for a valid last cluster to link.  */
629                     if (last_cluster)
630                     {
631 
632                         /* Normal condition - link the last cluster with the new
633                            found cluster.  */
634                         status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, FAT_index);
635 
636                         /* Check for a bad FAT write status.  */
637                         if (status !=  FX_SUCCESS)
638                         {
639 
640                             /* Return the bad status.  */
641                             return(status);
642                         }
643                     }
644 
645                     /* Otherwise, remember the new FAT index as the last.  */
646                     last_cluster =  FAT_index;
647 
648                     /* Move to the next FAT entry.  */
649                     FAT_index =  media_ptr -> fx_media_cluster_search_start;
650                 }
651 
652                 /* Place an end-of-file marker on the last cluster.  */
653                 status = _fx_utility_FAT_entry_write(media_ptr, last_cluster, media_ptr -> fx_media_fat_last);
654 
655                 /* Check for a bad FAT write status.  */
656                 if (status !=  FX_SUCCESS)
657                 {
658 
659                     /* Return the bad status.  */
660                     return(status);
661                 }
662 
663 #ifdef FX_FAULT_TOLERANT
664 
665                 /* Ensure the new FAT chain is properly written to the media.  */
666 
667                 /* Flush the cached individual FAT entries */
668                 _fx_utility_FAT_flush(media_ptr);
669 #endif
670 
671                 /* Now the new cluster needs to be linked to the sub-directory.  */
672                 if (search_dir_ptr)
673                 {
674                     cluster = search_dir_ptr -> fx_dir_entry_cluster;
675                 }
676                 else
677                 {
678                     cluster = media_ptr -> fx_media_root_cluster_32;
679                 }
680 
681                 /* Initialize loop variables.  */
682                 last_cluster =  0;
683                 i =  0;
684 
685                 /* Follow the link of FAT entries.  */
686                 while (cluster < media_ptr -> fx_media_fat_reserved)
687                 {
688 
689                     /* Read the current cluster entry from the FAT.  */
690                     status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &FAT_value);
691                     i++;
692 
693                     /* Check the return value.  */
694                     if (status != FX_SUCCESS)
695                     {
696 
697                         /* Return the error status.  */
698                         return(status);
699                     }
700 
701                     /* Determine if the FAT read was invalid.  */
702                     if ((cluster < FX_FAT_ENTRY_START) || (cluster == FAT_value) || (i > media_ptr -> fx_media_total_clusters))
703                     {
704 
705                         /* Return the bad status.  */
706                         return(FX_FAT_READ_ERROR);
707                     }
708 
709                     /* Save the last valid cluster.  */
710                     last_cluster =  cluster;
711 
712                     /* Setup for the next cluster.  */
713                     cluster =  FAT_value;
714                 }
715 
716                 /* Decrease the available clusters in the media.  */
717                 media_ptr -> fx_media_available_clusters =  media_ptr -> fx_media_available_clusters - clusters_needed;
718 
719                 /* Increase the number of directory entries.  */
720                 directory_entries =  directory_entries + ((clusters_needed * media_ptr -> fx_media_sectors_per_cluster) * media_ptr -> fx_media_bytes_per_sector) / FX_DIR_ENTRY_SIZE;
721 
722                 /* Determine if we need to reset the free entry start since we changed the
723                    number of directory entries.  If the last entry was not free, then we
724                    should definitely reset the free entry start.  */
725                 if (!(((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR) FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0)))
726                 {
727 
728                     /* Reset the free entry start to indicate we haven't found a starting free entry yet.  */
729                     free_entry_start =  directory_entries;
730                 }
731 
732                 /* Update the directory size field.  */
733                 directory_ptr -> fx_dir_entry_file_size =  directory_entries;
734 
735                 /* Defer the update of the FAT entry and the last cluster of the current
736                    directory entry until after the new cluster is initialized and written out.  */
737 
738                 /* Determine if a FAT32 is present.  */
739                 if ((media_ptr -> fx_media_32_bit_FAT) && (search_dir_ptr == FX_NULL))
740                 {
741 
742                     /* Change root directory entry count - FAT32 has a variable sized root directory.  */
743                     media_ptr -> fx_media_root_directory_entries =  directory_entries;
744                 }
745 
746                 /* At this point, link up the last cluster with the new cluster.  */
747                 status =  _fx_utility_FAT_entry_write(media_ptr, last_cluster, first_new_cluster);
748 
749                 /* Check the return value.  */
750                 if (status != FX_SUCCESS)
751                 {
752 
753                     /* Return the error status.  */
754                     return(status);
755                 }
756 
757 #ifdef FX_FAULT_TOLERANT
758 
759                 /* Flush the cached individual FAT entries */
760                 _fx_utility_FAT_flush(media_ptr);
761 #endif
762             }
763         }
764     } while (directory_index < directory_entries);
765 
766     /* Return FX_NO_MORE_SPACE status to the caller.  */
767     return(FX_NO_MORE_SPACE);
768 }
769 
770