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