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_entry_read                            PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    William E. Lamie, Microsoft Corporation                             */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    This function reads the supplied directory entry from the supplied  */
46 /*    source directory.  If the supplied directory entry is NULL, then    */
47 /*    the root directory is assumed.                                      */
48 /*                                                                        */
49 /*  INPUT                                                                 */
50 /*                                                                        */
51 /*    media_ptr                             Media control block pointer   */
52 /*    source_dir                            Source directory entry        */
53 /*    entry_ptr                             Directory entry number        */
54 /*    destination_ptr                       Pointer to destination for    */
55 /*                                            the directory entry         */
56 /*                                                                        */
57 /*  OUTPUT                                                                */
58 /*                                                                        */
59 /*    return status                                                       */
60 /*    *entry_ptr should point to the 8:3 entry if it is a long name       */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
65 /*    _fx_utility_logical_sector_read       Read directory sector         */
66 /*    _fx_utility_16_unsigned_read          Read a UINT from memory       */
67 /*    _fx_utility_32_unsigned_read          Read a ULONG from memory      */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    FileX System Functions                                              */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
78 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
79 /*                                            resulting in version 6.1    */
80 /*                                                                        */
81 /**************************************************************************/
_fx_directory_entry_read(FX_MEDIA * media_ptr,FX_DIR_ENTRY * source_dir,ULONG * entry_ptr,FX_DIR_ENTRY * destination_ptr)82 UINT  _fx_directory_entry_read(FX_MEDIA *media_ptr, FX_DIR_ENTRY *source_dir,
83                                ULONG *entry_ptr, FX_DIR_ENTRY *destination_ptr)
84 {
85 
86 UINT   i, j, card, dotflag, get_short_name;
87 UINT   number_of_lfns;
88 UINT   status;
89 ULONG  cluster, next_cluster = 0;
90 UINT   relative_cluster;
91 UINT   relative_sector;
92 ULONG  logical_sector;
93 ULONG  byte_offset;
94 ULONG  bytes_per_cluster;
95 UCHAR *read_ptr;
96 CHAR  *short_name_ptr;
97 ULONG  entry = *entry_ptr;
98 
99 
100 #ifndef FX_MEDIA_STATISTICS_DISABLE
101 
102     /* Increment the number of directory entry read requests.  */
103     media_ptr -> fx_media_directory_entry_reads++;
104 #endif
105 
106     /* Extended port-specific processing macro, which is by default defined to white space.  */
107     FX_DIRECTORY_ENTRY_READ_EXTENSION
108 
109     /* If trace is enabled, insert this event into the trace buffer.  */
110     FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_DIR_ENTRY_READ, media_ptr, 0, 0, 0, FX_TRACE_INTERNAL_EVENTS, 0, 0)
111 
112     /* Calculate the byte offset of this directory entry.  */
113     byte_offset =  entry * FX_DIR_ENTRY_SIZE;
114 
115     /* Determine if a sub-directory or FAT32 root directory is specified.  */
116     if ((source_dir) || (media_ptr -> fx_media_32_bit_FAT))
117     {
118 
119         /* Yes, a sub-directory is present.  */
120 
121         /* Calculate the number of bytes per cluster.  */
122         bytes_per_cluster =  ((ULONG)media_ptr -> fx_media_bytes_per_sector) *
123             ((ULONG)media_ptr -> fx_media_sectors_per_cluster);
124 
125         /* Check for invalid value.  */
126         if (bytes_per_cluster == 0)
127         {
128 
129             /* Invalid media, return error.  */
130             return(FX_MEDIA_INVALID);
131         }
132 
133         /* Now determine the relative cluster in the sub-directory file.  */
134         relative_cluster =   (UINT)(byte_offset / bytes_per_cluster);
135 
136         /* Calculate the byte offset within the cluster.  */
137         byte_offset =  byte_offset % bytes_per_cluster;
138 
139         /* Now figure out the relative sector within the cluster.  */
140         relative_sector =    (UINT)(byte_offset / ((ULONG)media_ptr -> fx_media_bytes_per_sector));
141 
142         /* Read the directory sector into the internal memory buffer.  */
143 
144         /* Determine if there is a sub-directory.  */
145         if (source_dir)
146         {
147 
148             /* Determine if this source directory has valid information from the previous call.  */
149             if ((source_dir -> fx_dir_entry_last_search_cluster) &&
150                 (source_dir -> fx_dir_entry_last_search_relative_cluster <= relative_cluster) &&
151                 (source_dir -> fx_dir_entry_last_search_log_sector == source_dir -> fx_dir_entry_log_sector) &&
152                 (source_dir -> fx_dir_entry_last_search_byte_offset == source_dir -> fx_dir_entry_byte_offset))
153             {
154 
155                 /* Use the previous information to start the search.  */
156                 cluster =  source_dir -> fx_dir_entry_last_search_cluster;
157 
158                 /* Setup the relative cluster index to the saved relative cluster.  */
159                 i =  source_dir -> fx_dir_entry_last_search_relative_cluster;
160 
161                 /* Clear the search cluster.  It will be updated prior to successful return.  */
162                 source_dir -> fx_dir_entry_last_search_cluster =  0;
163             }
164             else
165             {
166 
167                 /* Nothing from the previous directory read, just setup the starting cluster to the
168                    beginning of the sub-directory.  */
169                 cluster =  source_dir -> fx_dir_entry_cluster;
170 
171                 /* Setup the relative cluster index to zero.  */
172                 i =  0;
173             }
174         }
175         else
176         {
177 
178             /* No, setup the starting cluster to the FAT32 root cluster.  */
179             cluster =  media_ptr -> fx_media_root_cluster_32;
180 
181             /* Setup the relative cluster index to zero.  */
182             i =  0;
183         }
184 
185         /* Loop to position to the appropriate cluster.  */
186         while (i < relative_cluster)
187         {
188 
189             /* Check the value of the new cluster - it must be a valid cluster number
190                or something is really wrong!  */
191             if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved))
192             {
193 
194                 /* Send error message back to caller.  */
195                 return(FX_FILE_CORRUPT);
196             }
197 
198             /* Read the next cluster.  */
199             status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
200 
201             /* There is a potential for loop, but hardly anything can be done */
202 
203             /* Check for I/O error.  */
204             if (status != FX_SUCCESS)
205             {
206 
207                 /* Return error code.  */
208                 return(status);
209             }
210 
211             /* Setup the actual cluster.  */
212             cluster = next_cluster;
213 
214             /* Increment the relative cluster number.  */
215             i++;
216         }
217 
218         /* At this point, the directory data sector needs to be read.  */
219         logical_sector =    ((ULONG)media_ptr -> fx_media_data_sector_start) +
220             (((ULONG)cluster - FX_FAT_ENTRY_START) *
221              ((ULONG)media_ptr -> fx_media_sectors_per_cluster)) +
222             relative_sector;
223 
224         /* Read the logical directory sector.  */
225         status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector,
226                                                   media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
227 
228         /* Determine if an error occurred.  */
229         if (status != FX_SUCCESS)
230         {
231 
232             /* Return error code.  */
233             return(status);
234         }
235 
236         /* Calculate the byte offset within this sector.  */
237         byte_offset =  byte_offset % media_ptr -> fx_media_bytes_per_sector;
238     }
239     else
240     {
241 
242         /* Read the entry from the root directory.  */
243 
244         /* Determine which sector the requested root directory entry is in.  */
245         logical_sector =  (byte_offset / media_ptr -> fx_media_bytes_per_sector) +
246             (ULONG)media_ptr -> fx_media_root_sector_start;
247 
248         /* Read the logical directory sector.  */
249         status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector,
250                                                   media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
251 
252         /* Determine if an error occurred.  */
253         if (status != FX_SUCCESS)
254         {
255 
256             /* Return error code.  */
257             return(status);
258         }
259 
260         /* Set the cluster and relative variables (not used in this case) to avoid any compiler
261            warnings.  */
262         relative_cluster =  relative_sector =  cluster =  0;
263 
264         /* Now calculate the byte offset into this sector.  */
265         byte_offset =  byte_offset -
266             ((logical_sector - (ULONG)media_ptr -> fx_media_root_sector_start) *
267              media_ptr -> fx_media_bytes_per_sector);
268     }
269 
270     /* Setup a pointer into the buffer.  */
271     read_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT)byte_offset;
272 
273     /* Save the logical sector and byte offset in the returned directory entry.  */
274     destination_ptr -> fx_dir_entry_log_sector =       logical_sector;
275     destination_ptr -> fx_dir_entry_byte_offset =      byte_offset;
276 
277     /* Clear the short file name information.  */
278     destination_ptr -> fx_dir_entry_long_name_shorted =  0;
279     destination_ptr -> fx_dir_entry_short_name[0]     =  0;
280 
281     /* Setup short name pointer.  */
282     short_name_ptr =  destination_ptr -> fx_dir_entry_name;
283 
284     /* Check if long file name exists.  */
285     get_short_name =  0;
286     if ((*(read_ptr + 11) == (UCHAR)FX_LONG_NAME) && (*read_ptr != (UCHAR)FX_DIR_ENTRY_FREE))
287     {
288 
289         /* Collate the long name. */
290 
291         /* Pickup the file name length.  */
292         i = (((UINT)(*read_ptr & (UCHAR)0x1f) - 1) * FX_LONG_NAME_ENTRY_LEN) & 0xFFFFFFFF;
293 
294         /* Save the number of LFN entries.  */
295         number_of_lfns =  (UINT)(*read_ptr & (UCHAR)0x1f);
296 
297         /* Check the file name size.  */
298         if (i >= (FX_MAX_LONG_NAME_LEN - 1))
299         {
300 
301             /* Name is too big, shorten it.  */
302             get_short_name = 1;
303             destination_ptr -> fx_dir_entry_long_name_shorted =  (UINT)(*read_ptr & (UCHAR)0x1f);
304         }
305         else
306         {
307 
308             /* Size of name is fine, save pointer to short file name.  */
309             short_name_ptr = destination_ptr -> fx_dir_entry_short_name;
310 
311             /* Loop to make sure the long file name is NULL terminated.  */
312             j = i + FX_LONG_NAME_ENTRY_LEN + 1;
313             do
314             {
315                 /* Place a NULL in the long name.  */
316                 destination_ptr -> fx_dir_entry_name[i] =  0;
317 
318                 /* Position to the next entry.  */
319                 i++;
320             } while ((i < j) && (i < FX_MAX_LONG_NAME_LEN));
321         }
322 
323         /* Loop to pickup the rest of the name.  */
324         do
325         {
326 
327             /* Get the lower 5 bit containing the cardinality.  */
328             card = (UINT)(*read_ptr & (UCHAR)0x1f) - 1;
329 
330             /* For simplicity no checksum or cardinality checking is done */
331             if (get_short_name == 0)
332             {
333 
334                 /* Loop to pickup name.  */
335                 for (i = 1, j = 0; i < FX_DIR_ENTRY_SIZE; i += 2)
336                 {
337 
338                     if ((i == 11) || (i == 26))
339                     {
340                         continue;
341                     }
342 
343                     /* i = 12, 27 is not generated due to +=2 */
344                     if (i == 13)
345                     {
346                         i = 12;
347                         continue; /* this time next unicode is byte offset 14*/
348                     }
349 
350                     /* Determine if there is an actual unicode character present.  */
351                     if (read_ptr[i + 1])
352                     {
353 
354                         /* Extended byte is non-zero, make sure both bytes of the unicode entry are not
355                            all ones, since this is a normal case.  */
356                         if ((read_ptr[i + 1] != (UCHAR)0xFF) || (read_ptr[i] != (UCHAR)0xFF))
357                         {
358 
359                             /* Name is an actual unicode name, shorten it.  */
360                             get_short_name = 1;
361 
362                             /* Save the number of directory entries the LFN has.  This will be
363                                used later when updating the 8.3 portion of the LFN.  */
364                             destination_ptr -> fx_dir_entry_long_name_shorted =  number_of_lfns;
365 
366                             /* Setup short name pointer.  */
367                             short_name_ptr =  destination_ptr -> fx_dir_entry_name;
368                         }
369                     }
370 
371                     /* Determine if the character is NULL.  */
372                     if ((read_ptr[i] == FX_NULL) || (read_ptr[i] == (UCHAR)0xFF))
373                     {
374                         continue;
375                     }
376 
377                     /* Determine if the name is too big.  */
378                     if ((card * 13 + j) >= (FX_MAX_LONG_NAME_LEN - 1))
379                     {
380 
381                         /* Name is actually too big, shorten it.  */
382                         get_short_name =  1;
383 
384                         /* Save the number of directory entries the LFN has.  This will be
385                            used later when updating the 8.3 portion of the LFN.  */
386                         destination_ptr -> fx_dir_entry_long_name_shorted =  number_of_lfns;
387 
388                         /* Also reposition the short name pointer.  */
389                         short_name_ptr =  destination_ptr -> fx_dir_entry_name;
390 
391                         break;
392                     }
393 
394                     /* Each entry contains 13 unicode and first byte ASCII, second byte is extended. */
395                     destination_ptr -> fx_dir_entry_name[13 * card + j] = (CHAR)read_ptr[i];
396 
397                     j++;
398                 }
399             }
400 
401             /* Determine if a new sector needs to be read.  */
402             if (byte_offset + FX_DIR_ENTRY_SIZE >= media_ptr -> fx_media_bytes_per_sector)
403             {
404 
405                 /* Determine if a sub-directory or FAT32 root directory is specified.  */
406                 if ((source_dir) || (media_ptr -> fx_media_32_bit_FAT))
407                 {
408 
409                     /* Determine the next sector of the directory entry.  */
410                     if (relative_sector < (media_ptr -> fx_media_sectors_per_cluster - 1))
411                     {
412 
413                         /* More sectors in this cluster.  */
414 
415                         /* Simply increment the logical sector.  */
416                         logical_sector++;
417 
418                         /* Increment the relative sector.  */
419                         relative_sector++;
420                     }
421                     else
422                     {
423 
424                         /* We need to move to the next cluster.  */
425 
426                         /* Pickup the next cluster.  */
427                         status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
428 
429                         /* Check for I/O error.  */
430                         if (status != FX_SUCCESS)
431                         {
432 
433                             /* Return error code.  */
434                             return(status);
435                         }
436 
437                         /* Copy next cluster to the current cluster.  */
438                         cluster =  next_cluster;
439 
440                         /* Check the value of the new cluster - it must be a valid cluster number
441                            or something is really wrong!  */
442                         if ((cluster < FX_FAT_ENTRY_START) || (cluster >= media_ptr -> fx_media_fat_reserved))
443                         {
444 
445                             /* Send error message back to caller.  */
446                             return(FX_FILE_CORRUPT);
447                         }
448 
449                         /* Now increment the relative cluster.  */
450                         relative_cluster++;
451 
452                         /* Setup the relative sector (this is zero for subsequent cluster.  */
453                         relative_sector =  0;
454 
455                         /* Calculate the next logical sector.  */
456                         logical_sector =   ((ULONG)media_ptr -> fx_media_data_sector_start) +
457                             (((ULONG)cluster - FX_FAT_ENTRY_START) *
458                              ((ULONG)media_ptr -> fx_media_sectors_per_cluster));
459                     }
460                 }
461                 else
462                 {
463 
464                     /* Non-FAT 32 root directory.  */
465 
466                     /* Advance to the next sector.  */
467                     logical_sector++;
468 
469                     /* Determine if the logical sector is valid.  */
470                     if (logical_sector >= (ULONG)(media_ptr -> fx_media_root_sector_start + media_ptr -> fx_media_root_sectors))
471                     {
472 
473                         /* Trying to read past root directory - send error message back to caller.  */
474                         return(FX_FILE_CORRUPT);
475                     }
476                 }
477 
478                 /* Read the new sector.  */
479                 status =  _fx_utility_logical_sector_read(media_ptr, (ULONG64) logical_sector,
480                                                           media_ptr -> fx_media_memory_buffer, ((ULONG) 1), FX_DIRECTORY_SECTOR);
481 
482                 /* Check I/O status.  */
483                 if (status != FX_SUCCESS)
484                 {
485                     return(status);
486                 }
487 
488                 /* Set the byte offset to 0 for new sector.  */
489                 byte_offset = 0;
490             }
491             else
492             {
493 
494                 /* Calculate the new byte offset.  */
495                 byte_offset += FX_DIR_ENTRY_SIZE;
496             }
497 
498             /* Calculate the next read pointer.  */
499             read_ptr =  (UCHAR *)media_ptr -> fx_media_memory_buffer + (UINT) byte_offset;
500 
501             /* Move to the next entry.  */
502             entry++;
503         } while (card > 0);
504 
505         /* Set flag indicating long file name is present.  */
506         destination_ptr -> fx_dir_entry_long_name_present = 1;
507     }
508     else
509     {
510         /* No long file name is present.  */
511         get_short_name = 1;
512     }
513 
514     /* Determine if we need to clear the long name flag.  */
515     if (get_short_name == 1)
516     {
517 
518         /* Clear the long name flag.  */
519         destination_ptr -> fx_dir_entry_long_name_present =  0;
520     }
521 
522     /* Pickup the short file name.  */
523     short_name_ptr[0] =  0;
524     dotflag =  0;
525     for (i = 0, j = 0; i < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); i++)
526     {
527 
528         /* Check for a NULL.  */
529         if ((CHAR)read_ptr[i] == 0)
530         {
531             break;
532         }
533 
534         /* Check for a dot.  This happens for the first two directory entries, no
535            extra dot is needed.  */
536         if ((CHAR)read_ptr[i] == '.')
537         {
538             dotflag =  2;
539         }
540 
541         /* Check for a space.  */
542         if ((CHAR)read_ptr[i] == ' ')
543         {
544             /* Put a dot if a character comes after space.  */
545             if (dotflag == 0)
546             {
547                 dotflag =  1;
548             }
549             continue;
550         }
551 
552         /* Check for the main short file name size.  */
553         if (i == FX_DIR_NAME_SIZE)
554         {
555             /* Check to see if we need to insert a dot.  */
556             if (dotflag == 0)
557             {
558                 dotflag =  1;
559             }
560         }
561 
562         /* Check to see if we need to add a dot.  */
563         if (dotflag == 1)
564         {
565             /* Add dot to short file name.  */
566             short_name_ptr[j++] =  '.';
567             dotflag =  2;    /* no more dot for spaces */
568         }
569 
570         /* Copy a character.  */
571         short_name_ptr[j] =  (CHAR)read_ptr[i];
572 
573         /* Increment size.  */
574         j++;
575     }
576 
577     /* Determine if a long file name is present and its associated short file
578        name is actually free.  */
579     if ((destination_ptr -> fx_dir_entry_long_name_present) && (((UCHAR)short_name_ptr[0]) == (UCHAR)FX_DIR_ENTRY_FREE))
580     {
581 
582         /* Yes, the short file name is really free even though long file name entries directly precede it.
583            In this case, simply place the free directory marker at the front of the long file name.  */
584         destination_ptr -> fx_dir_entry_name[0] =  (CHAR)FX_DIR_ENTRY_FREE;
585         short_name_ptr[0] =  (CHAR)0;
586     }
587 
588     /* Determine if the short name pointer is NULL while the read pointer is
589        non-NULL.  */
590     if ((short_name_ptr[0] == 0) && (read_ptr[0] == ' '))
591     {
592 
593         /* This condition can occur with an all blank volume name.  Simply
594            copy the volume name to the short name in this case.  */
595         for (j = 0; j < (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE); j++)
596         {
597 
598             /* Copy a byte of the volume name.  */
599             short_name_ptr[j] =  (CHAR)read_ptr[j];
600         }
601     }
602 
603     /* Set end of string to null.  */
604     short_name_ptr[j] = 0;
605 
606     /* Load up the destination directory entry.  */
607     read_ptr += (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE);
608 
609     /* Copy the attribute into the destination.  */
610     destination_ptr -> fx_dir_entry_attributes =  *read_ptr++;
611 
612     /* Pickup the reserved byte.  */
613     destination_ptr -> fx_dir_entry_reserved =  *read_ptr++;
614 
615     /* Check for an undocumented NT file name feature for optimizing the storage
616        of all lower case file names that otherwise are valid 8.3 file names. The
617        following reserved bit definitions are present:
618 
619          BIT3 - set if 8.3 is all in lower case and no extended filename.
620          BIT4 - set for file, clear for directory entry if no extended filename.
621 
622        This is true for all NT systems. Prior to NT follows MSDOS FAT documentation and
623        is set to 0x00, all bits cleared. Therefore if BIT3 is set force lowercase.  */
624     if ((get_short_name) && (destination_ptr -> fx_dir_entry_reserved & 0x08))
625     {
626 
627         /* Microsoft undocumented NT file name feature... convert short name to lower
628            case.  */
629         for (j = 0; j <= (FX_DIR_NAME_SIZE + FX_DIR_EXT_SIZE) && (short_name_ptr[j] != 0x00); j++)
630         {
631 
632             /* Determine if an upper case character is present.  */
633             if ((short_name_ptr[j] >= 'A') && (short_name_ptr[j] <= 'Z'))
634             {
635 
636                 /* Yes, an upper case character is present. Force it to lower case.  */
637                 short_name_ptr[j] =  (CHAR)(short_name_ptr[j] + 32);
638             }
639         }
640     }
641 
642     /* Pickup the created time in milliseconds.  */
643     destination_ptr -> fx_dir_entry_created_time_ms =  *read_ptr++;
644 
645     /* Pickup the created time.  */
646     destination_ptr -> fx_dir_entry_created_time =  _fx_utility_16_unsigned_read(read_ptr);
647     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
648 
649     /* Pickup the created date.  */
650     destination_ptr -> fx_dir_entry_created_date =  _fx_utility_16_unsigned_read(read_ptr);
651     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
652 
653     /* Pickup the last accessed date.  */
654     destination_ptr -> fx_dir_entry_last_accessed_date =  _fx_utility_16_unsigned_read(read_ptr);
655     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
656 
657     /* read the upper 2 bytes of starting cluster - required only for 32 bit FAT */
658     if (media_ptr -> fx_media_32_bit_FAT)
659     {
660 
661         /* FAT32 only.  */
662         destination_ptr -> fx_dir_entry_cluster =  _fx_utility_16_unsigned_read(read_ptr);
663         destination_ptr -> fx_dir_entry_cluster <<= 16;
664     }
665     else
666     {
667         /* Not required for non FAT32.  */
668         destination_ptr -> fx_dir_entry_cluster =  0;
669     }
670 
671     /* Advance the read pointer.  */
672     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
673 
674     /* Copy the time into the destination.  */
675     destination_ptr -> fx_dir_entry_time =  _fx_utility_16_unsigned_read(read_ptr);
676     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
677 
678     /* Copy the date into the destination.  */
679     destination_ptr -> fx_dir_entry_date =  _fx_utility_16_unsigned_read(read_ptr);
680     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
681 
682     /* Copy the starting cluster into the destination.  */
683     destination_ptr -> fx_dir_entry_cluster +=  _fx_utility_16_unsigned_read(read_ptr);
684     read_ptr =  read_ptr + 2;  /* Always 2 bytes */
685 
686     /* Copy the file size into the destination.  */
687     destination_ptr -> fx_dir_entry_file_size =  _fx_utility_32_unsigned_read(read_ptr);
688 
689     /* Clear the destination search specific fields.  */
690     destination_ptr -> fx_dir_entry_last_search_cluster =           0;
691     destination_ptr -> fx_dir_entry_last_search_relative_cluster =  0;
692     destination_ptr -> fx_dir_entry_last_search_log_sector =        0;
693     destination_ptr -> fx_dir_entry_last_search_byte_offset =       0;
694 
695     /* Remember the entry number.  */
696     destination_ptr -> fx_dir_entry_number =  entry;
697 
698     /* Return entry number.  */
699     *entry_ptr =  entry;
700 
701     /* Determine if we should remember the last cluster and relative cluster.  */
702     if (source_dir)
703     {
704 
705         /* Yes, remember the last cluster and relative cluster for a subsequent call
706            to read a directory entry.  */
707         source_dir -> fx_dir_entry_last_search_cluster =           cluster;
708         source_dir -> fx_dir_entry_last_search_relative_cluster =  relative_cluster;
709 
710         /* Also remember several other items that are unique to the directory... just to verify that the
711            search information can be used.  */
712         source_dir -> fx_dir_entry_last_search_log_sector =        source_dir -> fx_dir_entry_log_sector;
713         source_dir -> fx_dir_entry_last_search_byte_offset =       source_dir -> fx_dir_entry_byte_offset;
714     }
715 
716     /* Return success to the caller.  */
717     return(FX_SUCCESS);
718 }
719 
720 
721 
722