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 #ifndef FX_NO_LOCAL_PATH
33 FX_LOCAL_PATH_SETUP
34 #endif
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _fx_directory_search                                PORTABLE C      */
42 /*                                                           6.1.10       */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    William E. Lamie, Microsoft Corporation                             */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function searches the media for the supplied name.  The search */
50 /*    routine will find files as well as directory names.                 */
51 /*                                                                        */
52 /*  INPUT                                                                 */
53 /*                                                                        */
54 /*    media_ptr                             Media control block pointer   */
55 /*    name_ptr                              Name pointer                  */
56 /*    entry_ptr                             Pointer to directory entry    */
57 /*                                            record                      */
58 /*    last_dir_ptr                          Pointer to destination for    */
59 /*                                            the last directory entry    */
60 /*    last_name_ptr                         Pointer to the last name      */
61 /*                                            token that was searched for */
62 /*                                                                        */
63 /*  OUTPUT                                                                */
64 /*                                                                        */
65 /*    return status                                                       */
66 /*                                                                        */
67 /*  CALLS                                                                 */
68 /*                                                                        */
69 /*    _fx_directory_name_extract            Extract directory name from   */
70 /*                                            input string                */
71 /*    _fx_directory_entry_read              Read entries from root dir    */
72 /*    _fx_utility_FAT_entry_read            Read FAT entries to calculate */
73 /*                                            the sub-directory size      */
74 /*                                                                        */
75 /*  CALLED BY                                                             */
76 /*                                                                        */
77 /*    FileX System Functions                                              */
78 /*                                                                        */
79 /*  RELEASE HISTORY                                                       */
80 /*                                                                        */
81 /*    DATE              NAME                      DESCRIPTION             */
82 /*                                                                        */
83 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
84 /*  09-30-2020     William E. Lamie         Modified comment(s), and      */
85 /*                                            added conditional to        */
86 /*                                            disable media search cache, */
87 /*                                            resulting in version 6.1    */
88 /*  06-02-2021     Bhupendra Naphade        Modified comment(s), and      */
89 /*                                            added check for             */
90 /*                                            volume label,               */
91 /*                                            resulting in version 6.1.7  */
92 /*  01-31-2022     William E. Lamie         Modified comment(s), and      */
93 /*                                            fixed path compare,         */
94 /*                                            resulting in version 6.1.10 */
95 /*                                                                        */
96 /**************************************************************************/
_fx_directory_search(FX_MEDIA * media_ptr,CHAR * name_ptr,FX_DIR_ENTRY * entry_ptr,FX_DIR_ENTRY * last_dir_ptr,CHAR ** last_name_ptr)97 UINT  _fx_directory_search(FX_MEDIA *media_ptr, CHAR *name_ptr, FX_DIR_ENTRY *entry_ptr,
98                            FX_DIR_ENTRY *last_dir_ptr, CHAR **last_name_ptr)
99 {
100 
101 ULONG         i, n;
102 UINT          found;
103 UINT          status;
104 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
105 UINT          v, j;
106 #endif /* FX_MEDIA_DISABLE_SEARCH_CACHE */
107 ULONG         cluster, next_cluster = 0;
108 ULONG64       directory_size;
109 CHAR         *dir_name_ptr;
110 CHAR         *work_ptr;
111 CHAR         *source_name_ptr;
112 CHAR         *destination_name_ptr;
113 FX_DIR_ENTRY  search_dir;
114 FX_DIR_ENTRY *search_dir_ptr;
115 CHAR         *name, alpha, name_alpha;
116 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
117 UINT          index;
118 CHAR         *path_ptr =  FX_NULL;
119 CHAR         *original_name =  name_ptr;
120 #endif
121 
122 #ifndef FX_MEDIA_STATISTICS_DISABLE
123 
124     /* Increment the number of directory search requests.  */
125     media_ptr -> fx_media_directory_searches++;
126 #endif
127 
128     /* Setup pointer to media name buffer.  */
129     name =  media_ptr -> fx_media_name_buffer;
130 
131     /* Setup the last directory, if required.  */
132     if (last_dir_ptr)
133     {
134 
135         /* Set the first character of the directory entry to NULL to
136            indicate root or no directory.  */
137         last_dir_ptr -> fx_dir_entry_name[0] =  0;
138     }
139 
140     /* Determine if the file name has a full directory path.  */
141     if ((*name_ptr == '\\') || (*name_ptr == '/'))
142     {
143 
144         /* Directory name has full path, set the search pointer to NULL.  */
145         search_dir_ptr =  FX_NULL;
146     }
147     else
148     {
149 
150         /* Set the initial search directory to the current working
151            directory - if there is one.  */
152 
153         /* First check for a local path pointer stored in the thread control block.  This
154            is only available in ThreadX Version 4 and above.  */
155 #ifndef FX_NO_LOCAL_PATH
156         if (_tx_thread_current_ptr -> tx_thread_filex_ptr)
157         {
158 
159             /* Determine if the local directory is not the root directory.  */
160             if (((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory.fx_dir_entry_name[0])
161             {
162 
163                 /* Start at the current working directory of the media.  */
164                 search_dir =   ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory;
165 
166 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
167 
168                 /* Setup pointer to the path.  */
169                 path_ptr =  ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string;
170 #endif
171 
172                 /* Set the internal pointer to the search directory as well.  */
173                 search_dir_ptr =  &search_dir;
174             }
175             else
176             {
177 
178                 /* We are searching in the root directory.  */
179                 search_dir_ptr =  FX_NULL;
180             }
181         }
182         else
183 #endif
184         if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0])
185         {
186 
187             /* Start at the current working directory of the media.  */
188             search_dir =  media_ptr -> fx_media_default_path.fx_path_directory;
189 
190 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
191 
192             /* Setup pointer to the path.  */
193             path_ptr =  media_ptr -> fx_media_default_path.fx_path_string;
194 #endif
195 
196             /* Set the internal pointer to the search directory as well.  */
197             search_dir_ptr =  &search_dir;
198         }
199         else
200         {
201 
202             /* The current default directory is the root so just set the
203                search directory pointer to NULL.  */
204             search_dir_ptr =  FX_NULL;
205         }
206     }
207 
208 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
209 
210     /* Determine if there is a previously found directory entry.  */
211     if (media_ptr -> fx_media_last_found_name[0])
212     {
213 
214     UINT  match;
215     CHAR *temp_ptr, beta;
216 
217         /* Yes, there is a previously found directory in our cache.  */
218 
219         /* Initialize the index.  */
220         v =  0;
221 
222         /* Determine if there is a full path.  */
223         if ((*name_ptr == '\\') || (*name_ptr == '/'))
224         {
225 
226             /* Yes, the full path is in the name buffer. Simply compare with what is in
227                the last search buffer.  */
228             while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (name_ptr[v]))
229             {
230 
231                 /* Pickup the respective name characters.  */
232                 alpha =  name_ptr[v];
233                 beta =   media_ptr -> fx_media_last_found_name[v];
234 
235                 /* Ensure directory markers are the same.  */
236                 if (alpha == '\\')
237                 {
238                     alpha =  '/';
239                 }
240                 if (beta == '\\')
241                 {
242                     beta =  '/';
243                 }
244 
245                 /* Is the name the same?  */
246                 if (alpha != beta)
247                 {
248 
249                     /* Break out of loop!  */
250                     break;
251                 }
252 
253                 /* Move to next character.  */
254                 v++;
255             }
256 
257             /* Determine if we have a match.  */
258             if (name_ptr[v] != media_ptr -> fx_media_last_found_name[v])
259             {
260                 match =  FX_FALSE;
261             }
262             else
263             {
264                 match =  FX_TRUE;
265             }
266         }
267         else
268         {
269 
270             /* Default to found.  */
271             match =  FX_TRUE;
272 
273             /* Determine if there is a default path to compare with.  */
274             if (path_ptr)
275             {
276 
277                 /* Yes, compare the current path with what is contained in the last
278                    found buffer. Note that the last found name must have at least one
279                    path separator as well as room for at least one character for a name. */
280                 while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (path_ptr[v]))
281                 {
282 
283                     /* Pickup the respective name characters.  */
284                     alpha =  media_ptr -> fx_media_last_found_name[v];
285                     beta =   path_ptr[v];
286 
287                     /* Ensure directory markers are the same.  */
288                     if (alpha == '\\')
289                     {
290                         alpha =  '/';
291                     }
292                     if (beta == '\\')
293                     {
294                         beta =  '/';
295                     }
296 
297                     /* Is the name the same?  */
298                     if (alpha != beta)
299                     {
300 
301                         /* Break out of loop!  */
302                         break;
303                     }
304 
305                     /* Move to next character.  */
306                     v++;
307                 }
308 
309                 /* Determine if we don't have a match...  The relative path must be exhausted. */
310                 if (path_ptr[v])
311                 {
312                     match =  FX_FALSE;
313                 }
314             }
315 
316             /* Determine if we still have a match.  */
317             if (match)
318             {
319 
320                 /* Now examine the rest of the last name and the newly supplied
321                    input name.  */
322 
323                 /* Determine if a valid directory separator is present.  */
324                 if ((media_ptr -> fx_media_last_found_name[v] != '\\') &&
325                     (media_ptr -> fx_media_last_found_name[v] != '/'))
326                 {
327 
328                     /* Set match to false - invalid directory path separator.  */
329                     match =  FX_FALSE;
330                 }
331                 else
332                 {
333                     /* Position past the next directory separator in the
334                        last name string.  */
335                     v++;
336                 }
337 
338                 /* Yes, the full path is in the name buffer. Simply compare with what is in
339                    the last search buffer.  */
340                 j =  0;
341                 while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (name_ptr[j]) && (match))
342                 {
343 
344                     /* Pickup the respective name characters.  */
345                     alpha =  name_ptr[j];
346                     beta =   media_ptr -> fx_media_last_found_name[v];
347 
348                     /* Ensure directory markers are the same.  */
349                     if (alpha == '\\')
350                     {
351                         alpha =  '/';
352                     }
353                     if (beta == '\\')
354                     {
355                         beta =  '/';
356                     }
357 
358                     /* Is the name the same?  */
359                     if (alpha != beta)
360                     {
361 
362                         /* Break out of loop!  */
363                         break;
364                     }
365 
366                     /* Move to next character.  */
367                     v++;
368                     j++;
369                 }
370 
371                 /* Avoid accessing fx_media_last_found_name out of bounds. */
372                 if (v >= 256)
373                 {
374                     match = FX_FALSE;
375                 }
376                 else if ((match) && (name_ptr[j] != media_ptr -> fx_media_last_found_name[v]))
377                 {
378 
379                     /* We don't have a match.  */
380                     match =  FX_FALSE;
381                 }
382             }
383         }
384 
385         /* Now determine if we actually found a match.  */
386         if (match)
387         {
388 
389             /* Save the directory entry name pointer.  */
390             temp_ptr =  entry_ptr -> fx_dir_entry_name;
391 
392             /* Copy the saved directory entry.  */
393             *entry_ptr =  media_ptr -> fx_media_last_found_entry;
394 
395             /* Restore the directory entry name pointer.  */
396             entry_ptr -> fx_dir_entry_name =  temp_ptr;
397 
398             /* Copy the directory name into the destination directory name.  */
399             for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++)
400             {
401 
402                 /* Copy character into the destination.  */
403                 temp_ptr[index] =  media_ptr -> fx_media_last_found_file_name[index];
404 
405                 /* See if we have copied the NULL termination character.  */
406                 if (temp_ptr[index] == (CHAR)FX_NULL)
407                 {
408 
409                     /* Determine if we should break here or at the top of the loop.  */
410                     if (index < (FX_MAX_LONG_NAME_LEN - 1))
411                     {
412 
413                         /* Yes, break out of the loop early.  */
414                         break;
415                     }
416                 }
417             }
418 
419             /* Determine if there is a search directory to copy.  */
420             if ((last_dir_ptr) && (media_ptr -> fx_media_last_found_directory_valid))
421             {
422 
423                 /* Yes, there was a search directory... and one is requested in this request as well.
424                    Simply copy it into the destination.  */
425 
426                 /* First, save the name pointer from the list directory pointer.  */
427                 destination_name_ptr =  last_dir_ptr -> fx_dir_entry_name;
428 
429                 /* Copy the entire directory entry structure.  */
430                 *last_dir_ptr =  media_ptr -> fx_media_last_found_directory;
431 
432                 /* Restore the original name buffer pointer.  */
433                 last_dir_ptr -> fx_dir_entry_name =  destination_name_ptr;
434 
435                 /* Pickup pointer to name to copy.  */
436                 source_name_ptr =  media_ptr -> fx_media_last_found_directory.fx_dir_entry_name;
437 
438                 /* Loop to copy the name into the last directory name buffer.  */
439                 for (n = 0; n < FX_MAX_LONG_NAME_LEN; n++)
440                 {
441 
442                     /* Copy a character.  */
443                     destination_name_ptr[n] =  source_name_ptr[n];
444 
445                     /* See if we have copied the NULL termination character.  */
446                     if (source_name_ptr[n] == (CHAR)FX_NULL)
447                     {
448 
449                         /* Determine if we should break here or at the top of the loop.  */
450                         if (n < (FX_MAX_LONG_NAME_LEN - 1))
451                         {
452 
453                             /* Yes, break out of the loop early.  */
454                             break;
455                         }
456                     }
457                 }
458             }
459 
460             /* Return the last name pointer, if required.  */
461             if (last_name_ptr)
462             {
463 
464                 /* Just set the last name to initial name string.  */
465                 *last_name_ptr =  temp_ptr;
466             }
467 
468 #ifndef FX_MEDIA_STATISTICS_DISABLE
469 
470             /* Increment the number of directory search cache hits.  */
471             media_ptr -> fx_media_directory_search_cache_hits++;
472 #endif
473 
474             /* Return success.  */
475             return(FX_SUCCESS);
476         }
477     }
478 
479     /* Not a sequential search, invalidate the saved information.  */
480     media_ptr -> fx_media_last_found_name[0] =  FX_NULL;
481 
482 #ifndef FX_MEDIA_STATISTICS_DISABLE
483 
484     /* If trace is enabled, insert this event into the trace buffer.  */
485     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_CACHE_MISS, media_ptr, media_ptr -> fx_media_directory_searches - media_ptr -> fx_media_directory_search_cache_hits, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
486 #else
487 
488     /* If trace is enabled, insert this event into the trace buffer.  */
489     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_CACHE_MISS, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
490 #endif
491 #endif
492 
493     /* Loop to traverse the directory paths to find the specified file.  */
494     do
495     {
496 
497         /* Remember the last name pointer, if required.  */
498         if (last_name_ptr)
499         {
500 
501             /* Just set the last name to initial name string.  */
502             *last_name_ptr =  name_ptr;
503         }
504 
505         /* Extract file name.  */
506         name_ptr =  _fx_directory_name_extract(name_ptr, name);
507 
508         /* Calculate the directory size.  */
509         if (search_dir_ptr)
510         {
511 
512             /* Ensure that the search directory's last search cluster is cleared.  */
513             search_dir_ptr -> fx_dir_entry_last_search_cluster =  0;
514 
515             /* Calculate the directory size by counting the allocated
516             clusters for it.  */
517             i =        0;
518             cluster =  search_dir_ptr -> fx_dir_entry_cluster;
519             while (cluster < media_ptr -> fx_media_fat_reserved)
520             {
521 
522                 /* Increment the cluster count.  */
523                 i++;
524 
525                 /* Read the next FAT entry.  */
526                 status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
527 
528                 /* Check the return status.  */
529                 if (status != FX_SUCCESS)
530                 {
531 
532                     /* Return the bad status.  */
533                     return(status);
534                 }
535 
536                 /* Check for error situation.  */
537                 if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters))
538                 {
539 
540                     /* Return the bad status.  */
541                     return(FX_FAT_READ_ERROR);
542                 }
543 
544                 cluster = next_cluster;
545             }
546 
547             /* Now we can calculate the directory size.  */
548             directory_size =  (((ULONG64) media_ptr -> fx_media_bytes_per_sector) *
549                                ((ULONG64) media_ptr -> fx_media_sectors_per_cluster) * i)
550                                 / (ULONG64) FX_DIR_ENTRY_SIZE;
551 
552             /* Also save this in the directory entry so we don't have to
553                calculate it later.  */
554             search_dir_ptr -> fx_dir_entry_file_size =  directory_size;
555 
556             /* If required, copy the last search directory entry into the
557                destination.  */
558             if (last_dir_ptr)
559             {
560 
561                 /* Copy the last search directory into the destination.  */
562 
563                 /* First, save the name pointer from the list directory pointer.  */
564                 destination_name_ptr =  last_dir_ptr -> fx_dir_entry_name;
565 
566                 /* Copy the entire directory entry structure.  */
567                 *last_dir_ptr =  *search_dir_ptr;
568 
569                 /* Restore the original name buffer pointer.  */
570                 last_dir_ptr -> fx_dir_entry_name =  destination_name_ptr;
571 
572                 /* Pickup pointer to name to copy.  */
573                 source_name_ptr =  search_dir_ptr -> fx_dir_entry_name;
574 
575                 /* Loop to copy the name into the last directory name buffer.  */
576                 for (n = 0; n < FX_MAX_LONG_NAME_LEN; n++)
577                 {
578 
579                     /* Copy a character.  */
580                     destination_name_ptr[n] =  source_name_ptr[n];
581 
582                     /* See if we have copied the NULL termination character.  */
583                     if (source_name_ptr[n] == (CHAR) FX_NULL)
584                     {
585 
586                         /* Determine if we should break here or at the top of the loop.  */
587                         if (n < (FX_MAX_LONG_NAME_LEN - 1))
588                         {
589 
590                             /* Yes, break out of the loop early.  */
591                             break;
592                         }
593                     }
594                 }
595             }
596         }
597         else
598         {
599 
600             /* Directory size is the number of entries in the root directory.  */
601             directory_size =  (ULONG)media_ptr -> fx_media_root_directory_entries;
602         }
603 
604         /* Loop through entries in the directory.  Yes, this is a
605            linear search!  */
606         i =      0;
607         found =  FX_FALSE;
608 
609 
610         do
611         {
612 
613             /* Read an entry from the directory.  */
614             status =  _fx_directory_entry_read(media_ptr, search_dir_ptr, &i, entry_ptr);
615 
616             i++;
617 
618             /* Check for error status.  */
619             if (status != FX_SUCCESS)
620             {
621                 return(status);
622             }
623 
624             /* Determine if this is the last directory entry.  */
625             if ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_DONE)
626             {
627                 break;
628             }
629 
630             /* Determine if the entry is a volume label entry */
631             if ((entry_ptr -> fx_dir_entry_attributes & FX_VOLUME))
632             {
633                 continue;
634             }
635 
636             /* Determine if this is an empty entry.  */
637             if (((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0))
638             {
639                 continue;
640             }
641 
642             /* Compare the input name and extension with the directory
643                entry.  */
644             work_ptr =      &name[0];
645             dir_name_ptr =  &(entry_ptr -> fx_dir_entry_name[0]);
646 
647             /* Loop to compare names.  */
648             do
649             {
650 
651                 /* Pickup character of directory name.  */
652                 alpha =  *dir_name_ptr;
653 
654                 /* Pickup character of name.  */
655                 name_alpha =  *work_ptr;
656 
657                 /* Determine if its case needs to be changed.  */
658                 if ((alpha >= 'a') && (alpha <= 'z'))
659                 {
660 
661                     /* Yes, make upper case.  */
662                     alpha =  (CHAR)((INT)alpha - 0x20);
663                 }
664 
665                 /* Determine if its case needs to be changed.  */
666                 if ((name_alpha >= 'a') && (name_alpha <= 'z'))
667                 {
668 
669                     /* Yes, make upper case.  */
670                     name_alpha =  (CHAR)((INT)name_alpha - 0x20);
671                 }
672 
673                 /* Compare name with directory name.  */
674                 if (alpha != name_alpha)
675                 {
676 
677                     /* The names don't match, get out of the loop. */
678                     break;
679                 }
680 
681                 /* Otherwise, increment the name pointers.  */
682                 work_ptr++;
683                 dir_name_ptr++;
684             } while (*dir_name_ptr);
685 
686             /* Determine if the requested name has been found.  If so,
687                return success to the caller.  */
688             if ((*dir_name_ptr == 0) && (*work_ptr == *dir_name_ptr))
689             {
690 
691                 /* Yes, the name was located.  All pertinent directory
692                    information is in the directory entry field.  */
693                 found =  FX_TRUE;
694             }
695             /* Determine if there is a short name to check.  */
696             else if (entry_ptr -> fx_dir_entry_short_name[0] != 0)
697             {
698 
699                 /* Yes, check for the short part of the name.  */
700 
701                 /* Compare the input name and extension with the directory entry.  */
702                 work_ptr =      &name[0];
703                 dir_name_ptr =  &(entry_ptr -> fx_dir_entry_short_name[0]);
704 
705                 /* Loop to compare names.  */
706                 do
707                 {
708 
709                     /* Pickup character of directory name.  */
710                     alpha =  *dir_name_ptr;
711 
712                     /* Pickup character of name.  */
713                     name_alpha =  *work_ptr;
714 
715                     /* Determine if its case needs to be changed.  */
716                     if ((name_alpha >= 'a') && (name_alpha <= 'z'))
717                     {
718 
719                         /* Yes, make upper case.  */
720                         name_alpha =  (CHAR)((INT)name_alpha - 0x20);
721                     }
722 
723                     /* Compare name with directory name.  */
724                     if (alpha != name_alpha)
725                     {
726 
727                         /* The names don't match, get out of the loop. */
728                         break;
729                     }
730 
731                     /* Otherwise, move the name pointers and increment the
732                        count.  */
733                     work_ptr++;
734                     dir_name_ptr++;
735                 } while (*dir_name_ptr);
736 
737                 /* Determine if the names match.  */
738                 if ((*dir_name_ptr == 0) && (*work_ptr == *dir_name_ptr))
739                 {
740 
741                     /* Yes, the name was located.  All pertinent directory
742                         information is in the directory entry field.  */
743                     found =  FX_TRUE;
744                 }
745             }
746         } while ((i < directory_size) && (!found));
747 
748         /* Now determine if we have a match.  */
749         if (!found)
750         {
751 
752             /* Return a "not found" status to the caller.  */
753             return(FX_NOT_FOUND);
754         }
755 
756         /* Determine if the found entry is indeed a sub-directory.  */
757         if (entry_ptr -> fx_dir_entry_attributes & FX_DIRECTORY)
758         {
759 
760             /* Move the directory search pointer to this entry.  */
761             search_dir =      *entry_ptr;
762             search_dir_ptr =  &search_dir;
763 
764             /* Ensure that the search directory's last search cluster is cleared.  */
765             search_dir_ptr -> fx_dir_entry_last_search_cluster =  0;
766 
767             /* Now determine if the new search directory is the root
768                directory.  */
769             if (!search_dir_ptr -> fx_dir_entry_cluster)
770             {
771 
772                 /* This is a backward link to the root directory.  Make
773                    sure this is indicated in the search directory
774                    information.  */
775                 search_dir_ptr -> fx_dir_entry_name[0] =  0;
776 
777                 /* Determine if we need to remember this in the last
778                    directory searched return area.  */
779                 if (last_dir_ptr)
780                 {
781 
782                     /* Yes, return this value to the caller.  */
783 
784                     /* First, save the name pointer from the list directory pointer.  */
785                     destination_name_ptr =  last_dir_ptr -> fx_dir_entry_name;
786 
787                     /* Copy the entire directory entry structure.  */
788                     *last_dir_ptr =  *search_dir_ptr;
789 
790                     /* Restore the original name buffer pointer.  */
791                     last_dir_ptr -> fx_dir_entry_name =  destination_name_ptr;
792 
793                     /* Pickup pointer to name to copy.  */
794                     source_name_ptr =  search_dir_ptr -> fx_dir_entry_name;
795 
796                     /* Loop to copy the name into the last directory name buffer.  */
797                     for (n = 0; n < FX_MAX_LONG_NAME_LEN; n++)
798                     {
799 
800                         /* Copy a character.  */
801                         destination_name_ptr[n] =  source_name_ptr[n];
802                     }
803                 }
804 
805                 /* Set the search directory pointer to NULL to indicate
806                    we are at the root directory.  */
807                 search_dir_ptr =  FX_NULL;
808             }
809         }
810         else
811         {
812 
813             /* This is not a directory, we better return not found
814                since we can't continue the search.  */
815             if (name_ptr)
816             {
817 
818                 /* Return not-found status to caller.  */
819                 return(FX_NOT_FOUND);
820             }
821         }
822     } while (name_ptr);
823 
824     /* If you reach this point, the directory is found absolutely, since !found will return directly in the loop above.   */
825 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
826 
827 
828     /* At this point, cache the found information.  If a subsequent search for the same name is done,
829        it will return immediately.  */
830 
831     /* Set the index of the saved name string.  */
832     v=  0;
833 
834     /* First, build the full path and name.  */
835     if ((*original_name != '\\') && (*original_name != '/') && (path_ptr))
836     {
837 
838         /* Copy the path into the destination.  */
839         while ((v< (FX_MAX_LAST_NAME_LEN - 1)) && (path_ptr[v]))
840         {
841 
842             /* Copy one character.   */
843             media_ptr -> fx_media_last_found_name[v] =  path_ptr[v];
844 
845             /* Move to next character.  */
846             v++;
847         }
848     }
849 
850     /* Now see if there is no directory path symbol in the name itself.  */
851     if ((*original_name != '\\') && (*original_name != '/'))
852     {
853 
854         /* If there is room, place a directory separator character.  */
855         if (v < (FX_MAX_LAST_NAME_LEN - 1))
856         {
857             media_ptr -> fx_media_last_found_name[v++] =  '/';
858         }
859     }
860 
861     /* Now append the name to the path.  */
862     j =  0;
863     while ((v < FX_MAX_LAST_NAME_LEN) && (original_name[j]))
864     {
865 
866         /* Copy one character.   */
867         media_ptr -> fx_media_last_found_name[v] =  original_name[j];
868 
869         /* Move to next character.  */
870         v++;
871         j++;
872     }
873 
874     /* Null terminate the last name string.   */
875     if (v< FX_MAX_LAST_NAME_LEN)
876     {
877 
878         /* Null terminate.  */
879         media_ptr -> fx_media_last_found_name[v] =  FX_NULL;
880     }
881     else
882     {
883 
884         /* The string is too big, NULL the string so it won't be used in searching.  */
885         media_ptr -> fx_media_last_found_name[0] =  FX_NULL;
886     }
887 
888     /* Determine if there is a search pointer.  */
889     if (search_dir_ptr)
890     {
891 
892         /* Yes, there is a search directory pointer so save it!   */
893         media_ptr -> fx_media_last_found_directory =  *search_dir_ptr;
894 
895         /* Indicate the search directory is valid.  */
896         media_ptr -> fx_media_last_found_directory_valid =  FX_TRUE;
897     }
898     else
899     {
900 
901         /* Indicate the search directory is not valid.  */
902         media_ptr -> fx_media_last_found_directory_valid =  FX_FALSE;
903     }
904 
905     /* Copy the directory entry.  */
906     media_ptr -> fx_media_last_found_entry =  *entry_ptr;
907 
908     /* Setup the directory entry for the last found internal file name.  */
909     media_ptr -> fx_media_last_found_entry.fx_dir_entry_name =  media_ptr -> fx_media_last_found_file_name;
910 
911     /* Copy the actual directory name into the cached directory name.  */
912     for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++)
913     {
914 
915         /* Copy character into the cached directory name.  */
916         media_ptr -> fx_media_last_found_file_name[index] =  entry_ptr ->  fx_dir_entry_name[index];
917 
918         /* See if we have copied the NULL termination character.  */
919         if (entry_ptr -> fx_dir_entry_name[index] == (CHAR)FX_NULL)
920         {
921 
922             /* Check to see if we use the break to get out of the loop.  */
923             if (index < (FX_MAX_LONG_NAME_LEN - 1))
924             {
925 
926                 /* Yes, not at the end of the string, break.  */
927                 break;
928             }
929         }
930     }
931 #endif
932 
933     return(FX_SUCCESS);
934 }
935 
936