lib_scandir.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /****************************************************************************
  2. * libs/libc/dirent/lib_scandir.c
  3. *
  4. * Copyright (C) 2019 Gregory Nutt. All rights reserved.
  5. * Author: Michael Jung <mijung@gmx.net>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <string.h>
  40. #include <dirent.h>
  41. #include <errno.h>
  42. #include <stdlib.h>
  43. #include "libc.h"
  44. /* The scandir() function is not appropriate for use within the kernel in its
  45. * current form because it uses user space memory allocators and modifies
  46. * the errno value.
  47. */
  48. #ifndef __KERNEL__
  49. /****************************************************************************
  50. * Public Functions
  51. ****************************************************************************/
  52. /****************************************************************************
  53. * Name: scandir
  54. *
  55. * Description:
  56. * The scandir() function scans the directory dirp, calling filter() on
  57. * each directory entry. Entries for which filter() returns nonzero are
  58. * stored in strings allocated via malloc(), sorted using qsort() with
  59. * comparison function compar(), and collected in array namelist which is
  60. * allocated via malloc(). If filter is NULL, all entries are selected.
  61. *
  62. * Input Parameters:
  63. * path - Pathname of the directory to scan
  64. * namelist - An array of pointers to directory entries, which is allocated
  65. * by scandir via malloc. Each directory entry is allocated via
  66. * malloc as well. The caller is responsible to free said
  67. * objects.
  68. * filter - Directory entries for which filter returns zero are not
  69. * included in the namelist. If filter is NULL, all entries are
  70. * included.
  71. * compar - Comparison function used with qsort() to sort the namelist.
  72. *
  73. * Returned Value:
  74. * If successful, the scandir() function returns the number of entries in
  75. * the namelist. Otherwise, it returns -1 and errno is set to indicate the
  76. * error.
  77. *
  78. ****************************************************************************/
  79. int scandir(FAR const char *path, FAR struct dirent ***namelist,
  80. CODE int (*filter)(FAR const struct dirent *),
  81. CODE int (*compar)(FAR const struct dirent **,
  82. FAR const struct dirent **))
  83. {
  84. FAR struct dirent *d;
  85. FAR struct dirent *dnew;
  86. FAR struct dirent **list = NULL;
  87. size_t listsize = 0;
  88. size_t cnt = 0;
  89. int errsv;
  90. int result;
  91. FAR DIR *dirp;
  92. /* This scandir implementation relies on errno being set by other service
  93. * functions that it is calling to figure if it was successful. We save
  94. * the original errno value to be able to restore it in case of success.
  95. */
  96. errsv = get_errno();
  97. dirp = opendir(path);
  98. if (!dirp)
  99. {
  100. return -1;
  101. }
  102. /* opendir might have set errno. Reset to zero. */
  103. set_errno(0);
  104. for (d = readdir(dirp); d != NULL; d = readdir(dirp))
  105. {
  106. size_t dsize;
  107. /* If the caller provided a filter function which tells scandir to skip
  108. * the current directory entry, do so.
  109. */
  110. if (filter && !filter(d))
  111. {
  112. continue;
  113. }
  114. /* The caller provided filter function might have set errno. Reset to
  115. * zero.
  116. */
  117. set_errno(0);
  118. /* Grow the directory entry list, if required. */
  119. if (cnt == listsize)
  120. {
  121. struct dirent **newlist;
  122. if (!listsize)
  123. {
  124. listsize = 4;
  125. }
  126. else
  127. {
  128. listsize *= 2;
  129. }
  130. newlist = lib_realloc(list, listsize * sizeof(*list));
  131. if (!newlist)
  132. {
  133. /* realloc failed and set errno. This will tell follow up code
  134. * that we failed.
  135. */
  136. break;
  137. }
  138. list = newlist;
  139. }
  140. /* Allocate a new directory entry, but restrict its heap size to what
  141. * is really required given the directories' path name.
  142. */
  143. dsize = (size_t)(&d->d_name[strlen(d->d_name) + 1] - (char *)d);
  144. dnew = lib_malloc(dsize);
  145. if (!dnew)
  146. {
  147. /* malloc failed and set errno. This will tell follow up code that
  148. * we failed.
  149. */
  150. break;
  151. }
  152. /* Copy directory entry to newly allocated one and update the list
  153. * accordingly.
  154. */
  155. memcpy(dnew, d, dsize);
  156. list[cnt] = dnew;
  157. cnt++;
  158. /* Some service function might have set errno as a side effect. Reset
  159. * to zero.
  160. */
  161. set_errno(0);
  162. }
  163. if (get_errno() == 0)
  164. {
  165. /* If the caller provided a comparison function, use it to sort the
  166. * list of directory entries.
  167. */
  168. if (compar)
  169. {
  170. typedef int (*compar_fn_t)(FAR const void *, FAR const void *);
  171. qsort(list, cnt, sizeof(*list), (compar_fn_t)compar);
  172. }
  173. /* Set the output parameters. */
  174. *namelist = list;
  175. result = (int)cnt;
  176. }
  177. else
  178. {
  179. size_t i;
  180. /* Something failed along the way. Clean up. */
  181. for (i = 0; i < cnt; i++)
  182. {
  183. lib_free(list[i]);
  184. }
  185. lib_free(list);
  186. result = -1;
  187. }
  188. closedir(dirp);
  189. if (result >= 0)
  190. {
  191. /* Restore original errno value in case of success. */
  192. set_errno(errsv);
  193. }
  194. return result;
  195. }
  196. #endif /* __KERNEL__ */