fs_opendir.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /****************************************************************************
  2. * fs/dirent/fs_opendir.c
  3. *
  4. * Licensed to the Apache Software Foundation (ASF) under one or more
  5. * contributor license agreements. See the NOTICE file distributed with
  6. * this work for additional information regarding copyright ownership. The
  7. * ASF licenses this file to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance with the
  9. * License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  15. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  16. * License for the specific language governing permissions and limitations
  17. * under the License.
  18. *
  19. ****************************************************************************/
  20. /****************************************************************************
  21. * Included Files
  22. ****************************************************************************/
  23. #include <nuttx/config.h>
  24. #include <stdbool.h>
  25. #include <string.h>
  26. #include <dirent.h>
  27. #include <assert.h>
  28. #include <errno.h>
  29. #include <nuttx/kmalloc.h>
  30. #include <nuttx/fs/fs.h>
  31. #include <nuttx/fs/dirent.h>
  32. #include "inode/inode.h"
  33. /****************************************************************************
  34. * Private Functions
  35. ****************************************************************************/
  36. /****************************************************************************
  37. * Name: open_mountpoint
  38. *
  39. * Description:
  40. * Handle the case where the inode to be opened is within a mountpoint.
  41. *
  42. * Input Parameters:
  43. * inode -- the inode of the mountpoint to open
  44. * relpath -- the relative path within the mountpoint to open
  45. * dir -- the dirent structure to be initialized
  46. *
  47. * Returned Value:
  48. * On success, OK is returned; Otherwise, a positive errno is returned.
  49. *
  50. ****************************************************************************/
  51. #ifndef CONFIG_DISABLE_MOUNTPOINT
  52. static inline int open_mountpoint(FAR struct inode *inode,
  53. FAR const char *relpath,
  54. FAR struct fs_dirent_s *dir)
  55. {
  56. int ret;
  57. /* The inode itself as the 'root' of mounted volume. The actually
  58. * directory is at relpath into the* mounted filesystem.
  59. *
  60. * Verify that the mountpoint inode supports the opendir() method
  61. */
  62. if (!inode->u.i_mops || !inode->u.i_mops->opendir)
  63. {
  64. return ENOSYS;
  65. }
  66. /* Take reference to the mountpoint inode. Note that we do not use
  67. * inode_addref() because we already hold the tree semaphore.
  68. */
  69. inode->i_crefs++;
  70. /* Perform the opendir() operation */
  71. ret = inode->u.i_mops->opendir(inode, relpath, dir);
  72. if (ret < 0)
  73. {
  74. /* We now need to back off our reference to the inode. We can't
  75. * call inode_release() to do that unless we release the tree
  76. * semaphore. The following should be safe because: (1) after the
  77. * reference count was incremented above it should be >=1 so it should
  78. * not decrement below zero, and (2) we hold the tree semaphore so no
  79. * other thread should be able to change the reference count.
  80. */
  81. inode->i_crefs--;
  82. DEBUGASSERT(inode->i_crefs >= 0);
  83. /* Negate the error value so that it can be used to set errno */
  84. return -ret;
  85. }
  86. return OK;
  87. }
  88. #endif
  89. /****************************************************************************
  90. * Name: open_pseudodir
  91. *
  92. * Description:
  93. * Handle the case where the inode to be opened is within the top-level
  94. * pseudo-file system.
  95. *
  96. * Input Parameters:
  97. * inode -- the inode of the mountpoint to open
  98. * dir -- the dirent structure to be initialized
  99. *
  100. * Returned Value:
  101. * None
  102. *
  103. ****************************************************************************/
  104. static void open_pseudodir(FAR struct inode *inode,
  105. FAR struct fs_dirent_s *dir)
  106. {
  107. /* We have a valid pseudo-filesystem node. Take two references on the
  108. * inode -- one for the parent (fd_root) and one for the child (fd_next).
  109. * Note that we do not call inode_addref because we are holding the tree
  110. * semaphore and that would result in deadlock.
  111. */
  112. inode->i_crefs += 2;
  113. dir->fd_root = inode; /* Save the inode where we start */
  114. dir->u.pseudo.fd_next = inode; /* This is the next node to use for readdir() */
  115. /* Flag the inode as belonging to the pseudo-filesystem */
  116. #ifndef CONFIG_DISABLE_MOUNTPOINT
  117. DIRENT_SETPSEUDONODE(dir->fd_flags);
  118. #endif
  119. }
  120. /****************************************************************************
  121. * Name: open_emptydir
  122. *
  123. * Description:
  124. * Handle the case where the inode to be opened is an empty, directory node
  125. * within the top-level pseudo-file system. That is, it has no operations
  126. * and, therefore, it must be a directory node. But is has no children
  127. * to be enumerated either.
  128. *
  129. * Input Parameters:
  130. * dir -- the dirent structure to be initialized
  131. *
  132. * Returned Value:
  133. * None
  134. *
  135. ****************************************************************************/
  136. static inline void open_emptydir(FAR struct fs_dirent_s *dir)
  137. {
  138. /* We have a valid, but empty pseudo-filesystem node. fd_next is NULL
  139. * meaning that we are already at the end of the list of its children.
  140. * fd_root is NULL so that if the directory is rewound, it will still be
  141. * at the end of the list.
  142. */
  143. #if 0 /* Already nullified by kumm_zalloc */
  144. dir->fd_root = NULL; /* Save the inode where we start */
  145. dir->u.pseudo.fd_next = NULL; /* We are at the end of the list */
  146. #endif
  147. /* Flag the inode as belonging to the pseudo-filesystem */
  148. #ifndef CONFIG_DISABLE_MOUNTPOINT
  149. DIRENT_SETPSEUDONODE(dir->fd_flags);
  150. #endif
  151. }
  152. /****************************************************************************
  153. * Public Functions
  154. ****************************************************************************/
  155. /****************************************************************************
  156. * Name: opendir
  157. *
  158. * Description:
  159. * The opendir() function opens a directory stream corresponding to the
  160. * directory name, and returns a pointer to the directory stream. The
  161. * stream is positioned at the first entry in the directory.
  162. *
  163. * Input Parameters:
  164. * path -- the directory to open
  165. *
  166. * Returned Value:
  167. * The opendir() function returns a pointer to the directory stream. On
  168. * error, NULL is returned, and errno is set appropriately.
  169. *
  170. * EACCES - Permission denied.
  171. * EMFILE - Too many file descriptors in use by process.
  172. * ENFILE - Too many files are currently open in the
  173. * system.
  174. * ENOENT - Directory does not exist, or name is an empty
  175. * string.
  176. * ENOMEM - Insufficient memory to complete the operation.
  177. * ENOTDIR - 'path' is not a directory.
  178. *
  179. ****************************************************************************/
  180. FAR DIR *opendir(FAR const char *path)
  181. {
  182. FAR struct inode *inode = NULL;
  183. FAR struct fs_dirent_s *dir;
  184. struct inode_search_s desc;
  185. #ifndef CONFIG_DISABLE_MOUNTPOINT
  186. FAR const char *relpath = NULL;
  187. #endif
  188. int ret;
  189. /* If we are given 'nothing' then we will interpret this as
  190. * request for the root inode.
  191. */
  192. SETUP_SEARCH(&desc, path, false);
  193. ret = inode_semtake();
  194. if (ret < 0)
  195. {
  196. ret = -ret;
  197. goto errout;
  198. }
  199. /* Find the node matching the path. */
  200. ret = inode_search(&desc);
  201. if (ret >= 0)
  202. {
  203. inode = desc.node;
  204. DEBUGASSERT(inode != NULL);
  205. #ifndef CONFIG_DISABLE_MOUNTPOINT
  206. relpath = desc.relpath;
  207. #endif
  208. }
  209. /* Did we get an inode? */
  210. if (inode == NULL)
  211. {
  212. /* Inode for 'path' does not exist. */
  213. ret = ENOTDIR;
  214. goto errout_with_semaphore;
  215. }
  216. /* Allocate a type DIR -- which is little more than an inode
  217. * container.
  218. */
  219. dir = (FAR struct fs_dirent_s *)kumm_zalloc(sizeof(struct fs_dirent_s));
  220. if (!dir)
  221. {
  222. /* Insufficient memory to complete the operation. */
  223. ret = ENOMEM;
  224. goto errout_with_semaphore;
  225. }
  226. /* Populate the DIR structure and return it to the caller. The way that
  227. * we do this depends on whenever this is a "normal" pseudo-file-system
  228. * inode or a file system mountpoint.
  229. */
  230. dir->fd_position = 0; /* This is the position in the read stream */
  231. /* Is this a node in the pseudo filesystem? Or a mountpoint? */
  232. #ifndef CONFIG_DISABLE_MOUNTPOINT
  233. if (INODE_IS_MOUNTPT(inode))
  234. {
  235. /* Yes, the node is a file system mountpoint */
  236. dir->fd_root = inode; /* Save the inode where we start */
  237. /* Open the directory at the relative path */
  238. ret = open_mountpoint(inode, relpath, dir);
  239. if (ret != OK)
  240. {
  241. goto errout_with_direntry;
  242. }
  243. }
  244. else
  245. #endif
  246. {
  247. /* The node is part of the root pseudo file system. Does the inode
  248. * have a child? If so that the child would be the 'root' of a list
  249. * of nodes under the directory.
  250. */
  251. FAR struct inode *child = inode->i_child;
  252. if (child != NULL)
  253. {
  254. /* It looks we have a valid pseudo-filesystem directory node. */
  255. open_pseudodir(child, dir);
  256. }
  257. else if (!inode->u.i_ops)
  258. {
  259. /* This is a dangling node with no children and no operations. Set
  260. * up to enumerate an empty directory.
  261. */
  262. open_emptydir(dir);
  263. }
  264. else
  265. {
  266. ret = ENOTDIR;
  267. goto errout_with_direntry;
  268. }
  269. }
  270. RELEASE_SEARCH(&desc);
  271. inode_semgive();
  272. return ((FAR DIR *)dir);
  273. /* Nasty goto's make error handling simpler */
  274. errout_with_direntry:
  275. kumm_free(dir);
  276. errout_with_semaphore:
  277. RELEASE_SEARCH(&desc);
  278. inode_semgive();
  279. errout:
  280. set_errno(ret);
  281. return NULL;
  282. }