1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** FileX Component                                                       */
16 /**                                                                       */
17 /**   Directory                                                           */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define FX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "fx_api.h"
28 #include "fx_system.h"
29 #include "fx_directory.h"
30 #include "fx_utility.h"
31 
32 #ifndef FX_NO_LOCAL_PATH
33 FX_LOCAL_PATH_SETUP
34 #endif
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _fx_directory_next_full_entry_find                  PORTABLE C      */
42 /*                                                           6.1          */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    William E. Lamie, Microsoft Corporation                             */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function returns the name of the next entry in the current     */
50 /*    working directory and various information about the name.  The      */
51 /*    function that returns the first name in the current directory must  */
52 /*    be called prior to this function.                                   */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    media_ptr                             Media control block pointer   */
57 /*    directory_name                        Destination for directory     */
58 /*                                            name                        */
59 /*    attributes                            Destination for attributes    */
60 /*    size                                  Destination for size          */
61 /*    year                                  Destination for year          */
62 /*    month                                 Destination for month         */
63 /*    day                                   Destination for day           */
64 /*    hour                                  Destination for hour          */
65 /*    minute                                Destination for minute        */
66 /*    second                                Destination for second        */
67 /*                                                                        */
68 /*  OUTPUT                                                                */
69 /*                                                                        */
70 /*    return status                                                       */
71 /*                                                                        */
72 /*  CALLS                                                                 */
73 /*                                                                        */
74 /*    _fx_directory_entry_read              Read entries from root dir    */
75 /*    _fx_utility_FAT_entry_read            Read FAT entries              */
76 /*                                                                        */
77 /*  CALLED BY                                                             */
78 /*                                                                        */
79 /*    Application Code and                                                */
80 /*    FileX System Functions                                              */
81 /*                                                                        */
82 /*  RELEASE HISTORY                                                       */
83 /*                                                                        */
84 /*    DATE              NAME                      DESCRIPTION             */
85 /*                                                                        */
86 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
87 /*  09-30-2020     William E. Lamie         Modified comment(s),          */
88 /*                                            resulting in version 6.1    */
89 /*                                                                        */
90 /**************************************************************************/
_fx_directory_next_full_entry_find(FX_MEDIA * media_ptr,CHAR * directory_name,UINT * attributes,ULONG * size,UINT * year,UINT * month,UINT * day,UINT * hour,UINT * minute,UINT * second)91 UINT  _fx_directory_next_full_entry_find(FX_MEDIA *media_ptr,
92                                          CHAR *directory_name, UINT *attributes, ULONG *size,
93                                          UINT *year, UINT *month, UINT *day, UINT *hour, UINT *minute, UINT *second)
94 {
95 
96 ULONG         i;
97 UINT          status;
98 UINT          temp_status;
99 ULONG         cluster, next_cluster = 0;
100 ULONG64       directory_size;
101 FX_DIR_ENTRY  entry;
102 FX_DIR_ENTRY *search_dir_ptr;
103 FX_PATH      *path_ptr;
104 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
105 UINT          index;
106 CHAR         *path_string_ptr =  FX_NULL;
107 #endif
108 
109 
110 #ifndef FX_MEDIA_STATISTICS_DISABLE
111 
112     /* Increment the number of times this service has been called.  */
113     media_ptr -> fx_media_directory_next_full_entry_finds++;
114 #endif
115 
116     /* Setup pointer to media name buffer.  */
117     entry.fx_dir_entry_name =  media_ptr -> fx_media_name_buffer + FX_MAX_LONG_NAME_LEN;
118 
119     /* Clear the short name string.  */
120     entry.fx_dir_entry_short_name[0] =  0;
121 
122 
123     /* Check the media to make sure it is open.  */
124     if (media_ptr -> fx_media_id != FX_MEDIA_ID)
125     {
126 
127         /* Return the media not opened error.  */
128         return(FX_MEDIA_NOT_OPEN);
129     }
130 
131     /* If trace is enabled, insert this event into the trace buffer.  */
132     FX_TRACE_IN_LINE_INSERT(FX_TRACE_DIRECTORY_NEXT_FULL_ENTRY_FIND, media_ptr, directory_name, 0, 0, FX_TRACE_DIRECTORY_EVENTS, 0, 0)
133 
134     /* Protect against other threads accessing the media.  */
135     FX_PROTECT
136 
137     /* First check for a local path pointer stored in the thread control block.  This
138        is only available in ThreadX Version 4 and above.  */
139 #ifndef FX_NO_LOCAL_PATH
140     if (_tx_thread_current_ptr -> tx_thread_filex_ptr)
141     {
142 
143         /* Setup the default path pointer.  */
144         path_ptr =  (FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr;
145 
146         /* Determine if we are at the root directory.  */
147         if (path_ptr -> fx_path_directory.fx_dir_entry_name[0])
148         {
149 
150             /* No, we are not at the root directory.  */
151 
152 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
153 
154             /* Setup pointer to the path.  */
155             path_string_ptr =  ((FX_PATH *)_tx_thread_current_ptr -> tx_thread_filex_ptr) -> fx_path_string;
156 #endif
157 
158             /* Set the internal pointer to the search directory as well.  */
159             search_dir_ptr =  &path_ptr -> fx_path_directory;
160         }
161         else
162         {
163 
164             /* The current default directory is the root so just set the
165                search directory pointer to NULL.  */
166             search_dir_ptr =  FX_NULL;
167         }
168     }
169     else
170 #endif
171 
172     /* Set the initial search directory to the current working
173        directory - if there is one.  */
174     if (media_ptr -> fx_media_default_path.fx_path_directory.fx_dir_entry_name[0])
175     {
176 
177         /* Setup the path pointer to the global media path.  */
178         path_ptr =  &media_ptr -> fx_media_default_path;
179 
180 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
181 
182         /* Setup pointer to the path.  */
183         path_string_ptr =  media_ptr -> fx_media_default_path.fx_path_string;
184 #endif
185 
186         /* Set the internal pointer to the search directory as well.  */
187         search_dir_ptr =  &path_ptr -> fx_path_directory;
188     }
189     else
190     {
191 
192         /* Setup the path pointer to the global media path.  */
193         path_ptr =  &media_ptr -> fx_media_default_path;
194 
195         /* The current default directory is the root so just set the
196            search directory pointer to NULL.  */
197         search_dir_ptr =  FX_NULL;
198     }
199 
200     /* Calculate the directory size.  */
201     if (search_dir_ptr)
202     {
203 
204 
205         /* Determine the directory size.  */
206         if (path_ptr -> fx_path_current_entry !=  0)
207         {
208 
209             /* Pickup the previously saved directory size.  */
210             directory_size =  search_dir_ptr -> fx_dir_entry_file_size;
211         }
212         else
213         {
214 
215             /* This should only be done on the first time into next directory find.  */
216 
217             /* Ensure that the search directory's last search cluster is cleared.  */
218             search_dir_ptr -> fx_dir_entry_last_search_cluster =  0;
219 
220             /* Calculate the directory size by counting the allocated
221             clusters for it.  */
222             i =        0;
223             cluster =  search_dir_ptr -> fx_dir_entry_cluster;
224             while (cluster < media_ptr -> fx_media_fat_reserved)
225             {
226 
227                 /* Increment the cluster count.  */
228                 i++;
229 
230                 /* Read the next FAT entry.  */
231                 status =  _fx_utility_FAT_entry_read(media_ptr, cluster, &next_cluster);
232 
233 
234                 /* Check the return status.  */
235                 if (status != FX_SUCCESS)
236                 {
237 
238                     /* Release media protection.  */
239                     FX_UNPROTECT
240 
241                     /* Return the bad status.  */
242                     return(status);
243                 }
244 
245                 if ((cluster < FX_FAT_ENTRY_START) || (cluster == next_cluster) || (i > media_ptr -> fx_media_total_clusters))
246                 {
247 
248                     /* Release media protection.  */
249                     FX_UNPROTECT
250 
251                     /* Return the bad status.  */
252                     return(FX_FAT_READ_ERROR);
253                 }
254 
255                 cluster = next_cluster;
256             }
257 
258             /* Now we can calculate the directory size.  */
259             directory_size =  (((ULONG64) media_ptr -> fx_media_bytes_per_sector) *
260                                ((ULONG64) media_ptr -> fx_media_sectors_per_cluster) * i)
261                                 / (ULONG64) FX_DIR_ENTRY_SIZE;
262 
263             /* Save how many entries there are in the directory.  */
264             search_dir_ptr -> fx_dir_entry_file_size =  directory_size;
265         }
266     }
267     else
268     {
269 
270         /* Directory size is the number of entries in the root directory.  */
271         directory_size =  (ULONG)media_ptr -> fx_media_root_directory_entries;
272     }
273 
274     /* Preset status with an error return.  */
275     status =  FX_NO_MORE_ENTRIES;
276 
277     /* Determine if the current entry is inside of the directory's range.  */
278     while (path_ptr -> fx_path_current_entry < directory_size)
279     {
280 
281         /* Read an entry from the directory.  */
282         temp_status = _fx_directory_entry_read(media_ptr, search_dir_ptr,
283                                                &(path_ptr -> fx_path_current_entry), &entry);
284 
285         /* Check for error status.  */
286         if (temp_status != FX_SUCCESS)
287         {
288 
289             /* Release media protection.  */
290             FX_UNPROTECT
291 
292             /* Return error status.  */
293             return(temp_status);
294         }
295 
296 
297         /* Check to see if the entry has something in it.  */
298         if (((UCHAR)entry.fx_dir_entry_name[0] == (UCHAR)FX_DIR_ENTRY_FREE) && (entry.fx_dir_entry_short_name[0] == 0))
299         {
300 
301             /* Current entry is free, skip to next entry and continue the loop.  */
302             path_ptr -> fx_path_current_entry++;
303             continue;
304         }
305 
306         /* Determine if a valid directory entry is present.  */
307         else if ((UCHAR)entry.fx_dir_entry_name[0] != (UCHAR)FX_DIR_ENTRY_DONE)
308         {
309 
310             /* A valid directory entry is present.  */
311 
312             /* Copy the name into the destination.  */
313             for (i = 0; entry.fx_dir_entry_name[i]; i++)
314             {
315 
316                 *directory_name =  entry.fx_dir_entry_name[i];
317                 directory_name++;
318             }
319 
320             /* Place a NULL at the end of the directory name.  */
321             *directory_name =  (CHAR)0;
322 
323             /* Determine if the entry's attributes are required.  */
324             if (attributes)
325             {
326 
327                 /* Pickup the entry's attributes.  */
328                 *attributes =  entry.fx_dir_entry_attributes;
329             }
330 
331             /* Determine if the entry's size is required.  */
332             if (size)
333             {
334 
335                 /* Pickup the entry's size.  */
336                 *size =  (ULONG)entry.fx_dir_entry_file_size;
337             }
338 
339             /* Determine if the entry's year is required.  */
340             if (year)
341             {
342 
343                 /* Pickup the entry's year.  */
344                 *year =  ((entry.fx_dir_entry_date >> FX_YEAR_SHIFT) & FX_YEAR_MASK) + FX_BASE_YEAR;
345             }
346 
347             /* Determine if the entry's month is required.  */
348             if (month)
349             {
350 
351                 /* Pickup the entry's month.  */
352                 *month =  (entry.fx_dir_entry_date >> FX_MONTH_SHIFT) & FX_MONTH_MASK;
353             }
354 
355             /* Determine if the entry's day is required.  */
356             if (day)
357             {
358 
359                 /* Pickup the entry's day.  */
360                 *day =  entry.fx_dir_entry_date & FX_DAY_MASK;
361             }
362 
363             /* Determine if the entry's hour is required.  */
364             if (hour)
365             {
366 
367                 /* Pickup the entry's hour.  */
368                 *hour =  (entry.fx_dir_entry_time >> FX_HOUR_SHIFT) & FX_HOUR_MASK;
369             }
370 
371             /* Determine if the entry's minute is required.  */
372             if (minute)
373             {
374 
375                 /* Pickup the entry's minute.  */
376                 *minute =  (entry.fx_dir_entry_time >> FX_MINUTE_SHIFT) & FX_MINUTE_MASK;
377             }
378 
379             /* Determine if the entry's second is required.  */
380             if (second)
381             {
382 
383                 /* Pickup the entry's second.  */
384                 *second =  (entry.fx_dir_entry_time & FX_SECOND_MASK) * 2;
385             }
386 
387             /* Increment the current entry for the media.  */
388             path_ptr -> fx_path_current_entry++;
389 
390 #ifndef FX_MEDIA_DISABLE_SEARCH_CACHE
391             {
392             UINT v, j;
393 
394 
395                 /* If a subsequent search for the same name is done, it will find it immediately.  */
396 
397                 /* Set the index of the saved name string.  */
398                 v =  0;
399 
400                 /* First, build the full path and name.  */
401                 if (path_string_ptr)
402                 {
403 
404                     /* Copy the path into the destination.  */
405                     while ((v < (FX_MAX_LAST_NAME_LEN - 1)) && (path_string_ptr[v]))
406                     {
407 
408                         /* Copy one character.   */
409                         media_ptr -> fx_media_last_found_name[v] =  path_string_ptr[v];
410 
411                         /* Move to next character.  */
412                         v++;
413                     }
414                 }
415 
416                 /* We know there is room at this point, place a directory separator character.  */
417                 media_ptr -> fx_media_last_found_name[v++] =  '/';
418 
419                 /* Now append the name to the path.  */
420                 j =  0;
421                 while ((v < FX_MAX_LAST_NAME_LEN) && (entry.fx_dir_entry_name[j]))
422                 {
423 
424                     /* Copy one character.   */
425                     media_ptr -> fx_media_last_found_name[v] =  entry.fx_dir_entry_name[j];
426 
427                     /* Move to next character.  */
428                     v++;
429                     j++;
430                 }
431 
432                 /* Null terminate the last name string.   */
433                 if (v < FX_MAX_LAST_NAME_LEN)
434                 {
435 
436                     /* Null terminate.  */
437                     media_ptr -> fx_media_last_found_name[v] =  FX_NULL;
438                 }
439                 else
440                 {
441 
442                     /* The string is too big, NULL the string so it won't be used in searching.  */
443                     media_ptr -> fx_media_last_found_name[0] =  FX_NULL;
444                 }
445 
446                 /* Determine if there is a search pointer.  */
447                 if (search_dir_ptr)
448                 {
449 
450                     /* Yes, there is a search directory pointer so save it!   */
451                     media_ptr -> fx_media_last_found_directory =  *search_dir_ptr;
452 
453                     /* Indicate the search directory is valid.  */
454                     media_ptr -> fx_media_last_found_directory_valid =  FX_TRUE;
455                 }
456                 else
457                 {
458 
459                     /* Indicate the search directory is not valid.  */
460                     media_ptr -> fx_media_last_found_directory_valid =  FX_FALSE;
461                 }
462 
463                 /* Copy the directory entry.  */
464                 media_ptr -> fx_media_last_found_entry =  entry;
465 
466                 /* Setup the last found directory entry to point at the last found internal file name.  */
467                 media_ptr -> fx_media_last_found_entry.fx_dir_entry_name =  media_ptr -> fx_media_last_found_file_name;
468 
469                 /* Copy the actual directory name into the cached directory name.  */
470                 for (index = 0; index < FX_MAX_LONG_NAME_LEN; index++)
471                 {
472 
473                     /* Copy character into the cached directory name.  */
474                     media_ptr -> fx_media_last_found_file_name[index] =  entry.fx_dir_entry_name[index];
475 
476                     /* See if we have copied the NULL termination character.  */
477                     if (entry.fx_dir_entry_name[index] == (CHAR)FX_NULL)
478                     {
479 
480                         /* Check to see if we use the break to get out of the loop.  */
481                         if (v < (FX_MAX_LONG_NAME_LEN - 1))
482                         {
483 
484                             /* Yes, not at the end of the string, break.  */
485                             break;
486                         }
487                     }
488                 }
489             }
490 #endif
491 
492             /* Set return status to success.  */
493             status =  FX_SUCCESS;
494 
495             /* Get out of the loop.  */
496             break;
497         }
498         else
499         {
500             /* Set the error code.  */
501             status =  FX_NO_MORE_ENTRIES;
502 
503             /* Get out of the loop.  */
504             break;
505         }
506     }
507 
508     /* Release media protection.  */
509     FX_UNPROTECT
510 
511     /* Return status to the caller.  */
512     return(status);
513 }
514 
515