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 #ifndef FX_NO_LOCAL_PATH
32 FX_LOCAL_PATH_SETUP
33 #endif
34 
35 
36 /* Define several Unicode working arrays...  This keeps the data structures
37    off the local stack.  */
38 
39 UCHAR _fx_unicode_temp_long_file_name[FX_MAX_LONG_NAME_LEN];
40 UCHAR _fx_unicode_search_name[FX_MAX_LONG_NAME_LEN * 2];
41 
42 
43 /**************************************************************************/
44 /*                                                                        */
45 /*  FUNCTION                                               RELEASE        */
46 /*                                                                        */
47 /*    _fx_unicode_directory_search                        PORTABLE C      */
48 /*                                                           6.1          */
49 /*  AUTHOR                                                                */
50 /*                                                                        */
51 /*    William E. Lamie, Microsoft Corporation                             */
52 /*                                                                        */
53 /*  DESCRIPTION                                                           */
54 /*                                                                        */
55 /*    This function searches for the specified unicode or short name.     */
56 /*                                                                        */
57 /*    Note: The buffer of short_name and unicode_name must be valid to    */
58 /*    fill search result. When short_name is a zero length string, search */
59 /*    is based on unicode string (terminated with NULL). If it's found    */
60 /*    the short name is written back to buffer of short_name. In this case*/
61 /*    unicode_name_buffer_length is ignored and short_name_buffer_length  */
62 /*    must not be zero to specify the buffer length. If buffer is too     */
63 /*    smallfor the result, overflow characters and NULL-terminator are cut*/
64 /*    off. When short_name is a valid string, search is based on          */
65 /*    short_name (terminated with NULL). If it's found the unicode name is*/
66 /*    written back to buffer of unicode_name. In this case                */
67 /*    short_name_buffer_length is ignored and unicode_name_buffer_length  */
68 /*    must not be zero to specify the unicode buffer length. If buffer is */
69 /*    too small for the result, overflow characters are cut off but       */
70 /*    NULL-terminator is kept.                                            */
71 /*                                                                        */
72 /*  INPUT                                                                 */
73 /*                                                                        */
74 /*    media_ptr                             Pointer to media              */
75 /*    short_name                            Pointer to short name         */
76 /*    short_name_buffer_length              Buffer length for short name  */
77 /*    unicode_name                          Pointer to Unicode name       */
78 /*    unicode_name_length                   Unicode name length           */
79 /*    unicode_name_buffer_length            Buffer length for Unicode name*/
80 /*                                                                        */
81 /*  OUTPUT                                                                */
82 /*                                                                        */
83 /*    Completion Status                                                   */
84 /*                                                                        */
85 /*  CALLS                                                                 */
86 /*                                                                        */
87 /*    _fx_unicode_directory_entry_read      Read a full unicode directory */
88 /*                                            entry                       */
89 /*    _fx_utility_FAT_entry_read            Read a FAT entry              */
90 /*                                                                        */
91 /*  CALLED BY                                                             */
92 /*                                                                        */
93 /*    Unicode Utilities                                                   */
94 /*                                                                        */
95 /*  RELEASE HISTORY                                                       */
96 /*                                                                        */
97 /*    DATE              NAME                      DESCRIPTION             */
98 /*                                                                        */
99 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
100 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
101 /*                                            resulting in version 6.1    */
102 /*                                                                        */
103 /**************************************************************************/
_fx_unicode_directory_search(FX_MEDIA * media_ptr,FX_DIR_ENTRY * entry_ptr,UCHAR * short_name,ULONG short_name_buffer_length,UCHAR * unicode_name,ULONG * unicode_name_length,ULONG unicode_name_buffer_length)104 UINT  _fx_unicode_directory_search(FX_MEDIA *media_ptr, FX_DIR_ENTRY *entry_ptr,
105                                    UCHAR *short_name, ULONG short_name_buffer_length,
106                                    UCHAR *unicode_name, ULONG *unicode_name_length, ULONG unicode_name_buffer_length)
107 {
108 
109 ULONG         i, j;
110 UINT          status, found;
111 ULONG         cluster, next_cluster = 0;
112 ULONG         directory_size;
113 FX_DIR_ENTRY  search_dir;
114 FX_DIR_ENTRY *search_dir_ptr;
115 ULONG         unicode_search_length;
116 ULONG         local_unicode_name_length;
117 CHAR          unicode_to_short_name[13];
118 CHAR         *short_name_ptr;
119 
120 
121     /* Setup temp unicode name length.  */
122     local_unicode_name_length =  *unicode_name_length;
123 
124 #ifndef FX_MEDIA_STATISTICS_DISABLE
125 
126     /* Increment the number of directory search requests.  */
127     media_ptr -> fx_media_directory_searches++;
128 #endif
129 
130     /* Set the initial search directory to the current working
131        directory - if there is one.  */
132 
133     /* First check for a local path pointer stored in the thread control block.  This
134        is only available in ThreadX Version 4 and above.  */
135 #ifndef FX_NO_LOCAL_PATH
136     if (_tx_thread_current_ptr -> tx_thread_filex_ptr)
137     {
138 
139         /* Determine if the local directory is not the root directory.  */
140         if (((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory.fx_dir_entry_name[0])
141         {
142 
143             /* Start at the current working directory of the media.  */
144             search_dir =   ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_directory;
145 
146             /* Set the internal pointer to the search directory as well.  */
147             search_dir_ptr =  &search_dir;
148         }
149         else
150         {
151 
152             /* We are searching in the root directory.  */
153             search_dir_ptr =  FX_NULL;
154         }
155     }
156     else
157 #endif
158     if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0])
159     {
160 
161         /* Start at the current working directory of the media.  */
162         search_dir =  media_ptr -> fx_media_default_path.fx_path_directory;
163 
164         /* Set the internal pointer to the search directory as well.  */
165         search_dir_ptr =  &search_dir;
166     }
167     else
168     {
169 
170         /* The current default directory is the root so just set the
171            search directory pointer to NULL.  */
172         search_dir_ptr =  FX_NULL;
173     }
174 
175     /* Calculate the directory size.  */
176     if (search_dir_ptr)
177     {
178 
179         /* Ensure that the search directory's last search cluster is cleared.  */
180         search_dir_ptr -> fx_dir_entry_last_search_cluster =  0;
181 
182         /* Calculate the directory size by counting the allocated
183            clusters for it.  */
184         i =        0;
185         cluster =  search_dir_ptr -> fx_dir_entry_cluster;
186         while ((cluster >= FX_FAT_ENTRY_START) && (cluster < media_ptr -> fx_media_fat_reserved))
187         {
188 
189             /* Increment the cluster count.  */
190             i++;
191 
192             /* Read the next FAT entry.  */
193             status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
194 
195             /* Check the return status.  */
196             if (status != FX_SUCCESS)
197             {
198 
199                 /* Return the bad status.  */
200                 return(status);
201             }
202 
203             /* Check for error situation.  */
204             if ((cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters))
205             {
206 
207                 /* Return the bad status.  */
208                 return(FX_FAT_READ_ERROR);
209             }
210 
211             cluster = next_cluster;
212         }
213 
214         /* Now we can calculate the directory size.  */
215         directory_size =  (((ULONG)media_ptr -> fx_media_bytes_per_sector) *
216                            ((ULONG)media_ptr -> fx_media_sectors_per_cluster) * i) /
217                            (ULONG)FX_DIR_ENTRY_SIZE;
218 
219         /* Also save this in the directory entry so we don't have to
220            calculate it later.  */
221         search_dir_ptr -> fx_dir_entry_file_size =  directory_size;
222     }
223     else
224     {
225 
226         /* Directory size is the number of entries in the root directory.  */
227         directory_size =  (ULONG)media_ptr -> fx_media_root_directory_entries;
228     }
229 
230     /* Determine if we are searching for a short file name or a unicode file name.  */
231     if (short_name[0] == 0)
232     {
233 
234         /* If the unicode name fit into short name length, covert the Unicode to ASCII if possible.  */
235         if (local_unicode_name_length <= 13)
236         {
237             for (j = 0; j < local_unicode_name_length; j++)
238             {
239                 if ((unicode_name[j * 2] <= 0x7F) && (unicode_name[j * 2 + 1] == 0))
240                 {
241 
242                     unicode_to_short_name[j] = (CHAR)unicode_name[j * 2];
243                     if ((unicode_to_short_name[j] >= 'a') && (unicode_to_short_name[j] <= 'z'))
244                     {
245 
246                         /* Lower case, convert to upper case!  */
247                         unicode_to_short_name[j] =  (CHAR)((INT)unicode_to_short_name[j] - 0x20);
248                     }
249                 }
250                 else
251                 {
252                     unicode_to_short_name[0] = 0;
253                     break;
254                 }
255             }
256         }
257         else
258         {
259             unicode_to_short_name[0] = 0;
260         }
261     }
262     else
263     {
264         unicode_to_short_name[0] = 0;
265     }
266 
267     /* Loop through entries in the directory.  Yes, this is a
268        linear search!  */
269     i =      0;
270     do
271     {
272 
273         /* Read an entry from the directory.  */
274         status =  _fx_unicode_directory_entry_read(media_ptr, search_dir_ptr, &i, entry_ptr, &_fx_unicode_search_name[0], &unicode_search_length);
275         i++;
276 
277         /* Check for error status.  */
278         if (status != FX_SUCCESS)
279         {
280             return(status);
281         }
282 
283         /* Determine if this is an empty entry.  */
284         if (((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry_ptr -> fx_dir_entry_short_name[0] == 0))
285         {
286             continue;
287         }
288 
289         /* Determine if this is the last directory entry.  */
290         if ((UCHAR)entry_ptr -> fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_DONE)
291         {
292             break;
293         }
294 
295         /* Determine if there is a short name to match.  */
296         if (unicode_to_short_name[0])
297         {
298 
299             /* Get the short name pointer.  */
300             if (entry_ptr -> fx_dir_entry_short_name[0])
301             {
302                 short_name_ptr =  entry_ptr -> fx_dir_entry_short_name;
303             }
304             else
305             {
306                 short_name_ptr =  entry_ptr -> fx_dir_entry_name;
307             }
308 
309             for (j = 0; j < local_unicode_name_length; j++)
310             {
311 
312                 /* Compare characters.  */
313                 if (short_name_ptr[j] != unicode_to_short_name[j])
314                 {
315                     break;
316                 }
317             }
318             if (j == local_unicode_name_length)
319             {
320 
321                 /* Only the 1st 13 bytes or the buffer length is copied, whichever is smaller.  */
322                 if (short_name_buffer_length > 13)
323                 {
324                     short_name_buffer_length = 13;
325                 }
326 
327                 /* The names match, copy the short name into the destination.  */
328                 for (j = 0; j < short_name_buffer_length; j++)
329                 {
330                     short_name[j] = (UCHAR)short_name_ptr[j];
331                 }
332 
333                 /* Return success to caller.  */
334                 return(FX_SUCCESS);
335             }
336         }
337 
338         /* Determine if this is not a unicode name.  */
339         if (unicode_search_length == 0)
340         {
341             continue;
342         }
343 
344         /* Determine if we are searching for a short file name or a unicode file name.  */
345         if (short_name[0])
346         {
347 
348             /* We have a short name and need a unicode name.  Compare the short name against the short name in
349                the directory entry for a match.  */
350             found =  FX_TRUE;
351             for (j = 0; j < 12; j++)
352             {
353 
354                 /* Compare characters...  */
355                 if (entry_ptr -> fx_dir_entry_short_name[0])
356                 {
357 
358                     /* Yes, the return name is in the short name field... compare against it!  */
359                     if (short_name[j] != (UCHAR)entry_ptr -> fx_dir_entry_short_name[j])
360                     {
361 
362                         found = FX_FALSE;
363                         break;
364                     }
365                 }
366                 else
367                 {
368 
369                     /* No, the return name is in the name field... compare against it!  */
370                     if (short_name[j] != (UCHAR)entry_ptr -> fx_dir_entry_name[j])
371                     {
372 
373                         found = FX_FALSE;
374                         break;
375                     }
376                 }
377 
378                 /* Are we done?  */
379                 if (short_name[j] == (UCHAR)FX_NULL)
380                 {
381                     break;
382                 }
383             }
384 
385             /* One final compare to see if we have a match.  */
386             if ((found == FX_FALSE) || ((j == 12) && (short_name[12] != 0)))
387             {
388                 continue;
389             }
390 
391             /* A match was found so copy the unicode name and length and return.  */
392             /* Copy the length.  */
393             *unicode_name_length =  unicode_search_length;
394 
395             /* Check if the name fit in the buffer.  */
396             if (unicode_name_buffer_length < (unicode_search_length + 1) * 2)
397             {
398                 unicode_search_length = (unicode_name_buffer_length - 2) / 2;
399             }
400 
401             /* Copy the unicode name.   */
402             for (j = 0; j < unicode_search_length * 2; j++)
403             {
404 
405                 /* Copy one unicode character to the destination...  */
406                 unicode_name[j] =  _fx_unicode_search_name[j];
407             }
408 
409             /* Make sure there is a NULL in the destination.  */
410             unicode_name[j] =    (UCHAR)0;
411             unicode_name[j + 1] =  (UCHAR)0;
412 
413 
414 
415             /* Return successful completion.  */
416             return(FX_SUCCESS);
417         }
418         else
419         {
420 
421             /* Determine if this is the correct unicode name.  */
422             if (unicode_search_length != local_unicode_name_length)
423             {
424                 continue;
425             }
426 
427             /* Compare the unicode search name with the requested unicode name.  */
428             for (j = 0; j < (local_unicode_name_length * 2); j = j + 2)
429             {
430 
431                 /* Compare bytes of each unicode name.  */
432                 if (unicode_name[j] != _fx_unicode_search_name[j])
433                 {
434 
435                     /* Not match, Check if the character is in ASCII range.  */
436                     if ((_fx_unicode_search_name[j + 1] == 0) && (unicode_name[j + 1] == 0))
437                     {
438 
439                         /* Check if it is case mismatch.  */
440                         if ((unicode_name[j]) >= 'a' && (unicode_name[j] <= 'z'))
441                         {
442                             if ((unicode_name[j] - 0x20) == _fx_unicode_search_name[j])
443                             {
444                                 continue;
445                             }
446                         }
447                         if ((_fx_unicode_search_name[j]) >= 'a' && (_fx_unicode_search_name[j] <= 'z'))
448                         {
449                             if ((_fx_unicode_search_name[j] - 0x20) == unicode_name[j])
450                             {
451                                 continue;
452                             }
453                         }
454                     }
455 
456                     break;
457                 }
458 
459                 /* Compare the next byte.  */
460                 if (unicode_name[j + 1] != _fx_unicode_search_name[j + 1])
461                 {
462                     break;
463                 }
464             }
465 
466             /* Determine if the names do not match.  */
467             if (j != (local_unicode_name_length * 2))
468             {
469                 continue;
470             }
471 
472             /* Otherwise, the names match, copy the short name into the destination.  */
473             /* Only the 1st 13 bytes or the buffer length is copied, whichever is smaller.  */
474             if (short_name_buffer_length > 13)
475             {
476                 short_name_buffer_length = 13;
477             }
478             for (j = 0; j < short_name_buffer_length; j++)
479             {
480 
481                 /* Copy a character.  */
482                 if (entry_ptr -> fx_dir_entry_short_name[0])
483                 {
484                     short_name[j] =  (UCHAR)entry_ptr -> fx_dir_entry_short_name[j];
485                 }
486                 else
487                 {
488                     short_name[j] =  (UCHAR)entry_ptr -> fx_dir_entry_name[j];
489                 }
490             }
491 
492             /* Return success to caller.  */
493             return(FX_SUCCESS);
494         }
495     } while (i < directory_size);
496 
497     /* Return not found.  */
498     return(FX_NOT_FOUND);
499 }
500 
501