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