fs_opendir.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /****************************************************************************
  2. * fs/dirent/fs_opendir.c
  3. *
  4. * Copyright (C) 2007-2009, 2011, 2013-2014, 2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  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 <stdbool.h>
  40. #include <string.h>
  41. #include <dirent.h>
  42. #include <assert.h>
  43. #include <errno.h>
  44. #include <nuttx/kmalloc.h>
  45. #include <nuttx/fs/fs.h>
  46. #include <nuttx/fs/dirent.h>
  47. #include "inode/inode.h"
  48. /****************************************************************************
  49. * Private Functions
  50. ****************************************************************************/
  51. /****************************************************************************
  52. * Name: open_mountpoint
  53. *
  54. * Description:
  55. * Handle the case where the inode to be opened is within a mountpoint.
  56. *
  57. * Inputs:
  58. * inode -- the inode of the mountpoint to open
  59. * relpath -- the relative path within the mountpoint to open
  60. * dir -- the dirent structure to be initialized
  61. *
  62. * Return:
  63. * On success, OK is returned; Otherwise, a positive errno is returned.
  64. *
  65. ****************************************************************************/
  66. #ifndef CONFIG_DISABLE_MOUNTPOINT
  67. static inline int open_mountpoint(FAR struct inode *inode,
  68. FAR const char *relpath,
  69. FAR struct fs_dirent_s *dir)
  70. {
  71. int ret;
  72. /* The inode itself as the 'root' of mounted volume. The actually
  73. * directory is at relpath into the* mounted filesystem.
  74. *
  75. * Verify that the mountpoint inode supports the opendir() method
  76. */
  77. if (!inode->u.i_mops || !inode->u.i_mops->opendir)
  78. {
  79. return ENOSYS;
  80. }
  81. /* Take reference to the mountpoint inode. Note that we do not use
  82. * inode_addref() because we already hold the tree semaphore.
  83. */
  84. inode->i_crefs++;
  85. /* Perform the opendir() operation */
  86. ret = inode->u.i_mops->opendir(inode, relpath, dir);
  87. if (ret < 0)
  88. {
  89. /* We now need to back off our reference to the inode. We can't
  90. * call inode_release() to do that unless we release the tree
  91. * semaphore. The following should be safe because: (1) after the
  92. * reference count was incremented above it should be >=1 so it should
  93. * not decrement below zero, and (2) we hold the tree semaphore so no
  94. * other thread should be able to change the reference count.
  95. */
  96. inode->i_crefs--;
  97. DEBUGASSERT(inode->i_crefs >= 0);
  98. /* Negate the error value so that it can be used to set errno */
  99. return -ret;
  100. }
  101. return OK;
  102. }
  103. #endif
  104. /****************************************************************************
  105. * Name: open_pseudodir
  106. *
  107. * Description:
  108. * Handle the case where the inode to be opened is within the top-level
  109. * pseudo-file system.
  110. *
  111. * Inputs:
  112. * inode -- the inode of the mountpoint to open
  113. * dir -- the dirent structure to be initialized
  114. *
  115. * Return:
  116. * None
  117. *
  118. ****************************************************************************/
  119. static void open_pseudodir(FAR struct inode *inode, FAR struct fs_dirent_s *dir)
  120. {
  121. /* We have a valid pseudo-filesystem node. Take two references on the
  122. * inode -- one for the parent (fd_root) and one for the child (fd_next).
  123. * Note that we do not call inode_addref because we are holding the tree
  124. * semaphore and that would result in deadlock.
  125. */
  126. inode->i_crefs += 2;
  127. dir->fd_root = inode; /* Save the inode where we start */
  128. dir->u.pseudo.fd_next = inode; /* This is the next node to use for readdir() */
  129. /* Flag the inode as belonging to the pseudo-filesystem */
  130. #ifndef CONFIG_DISABLE_MOUNTPOINT
  131. DIRENT_SETPSEUDONODE(dir->fd_flags);
  132. #endif
  133. }
  134. /****************************************************************************
  135. * Name: open_emptydir
  136. *
  137. * Description:
  138. * Handle the case where the inode to be opened is an empty, directory node
  139. * within the top-level pseudo-file system. That is, it has no operations
  140. * and, therefore, it must be a directory node. But is has no children
  141. * to be enumerated either.
  142. *
  143. * Inputs:
  144. * dir -- the dirent structure to be initialized
  145. *
  146. * Return:
  147. * None
  148. *
  149. ****************************************************************************/
  150. static inline void open_emptydir(FAR struct fs_dirent_s *dir)
  151. {
  152. /* We have a valid, but empty pseudo-filesystem node. fd_next is NULL
  153. * meaning that we are already at the end of the list of its children.
  154. * fd_root is NULL so that if the directory is rewound, it will still be
  155. * at the end of the list.
  156. */
  157. #if 0 /* Already nullified by kumm_zalloc */
  158. dir->fd_root = NULL; /* Save the inode where we start */
  159. dir->u.pseudo.fd_next = NULL; /* We are at the end of the list */
  160. #endif
  161. /* Flag the inode as belonging to the pseudo-filesystem */
  162. #ifndef CONFIG_DISABLE_MOUNTPOINT
  163. DIRENT_SETPSEUDONODE(dir->fd_flags);
  164. #endif
  165. }
  166. /****************************************************************************
  167. * Public Functions
  168. ****************************************************************************/
  169. /****************************************************************************
  170. * Name: opendir
  171. *
  172. * Description:
  173. * The opendir() function opens a directory stream corresponding to the
  174. * directory name, and returns a pointer to the directory stream. The
  175. * stream is positioned at the first entry in the directory.
  176. *
  177. * Inputs:
  178. * path -- the directory to open
  179. *
  180. * Return:
  181. * The opendir() function returns a pointer to the directory stream. On
  182. * error, NULL is returned, and errno is set appropriately.
  183. *
  184. * EACCES - Permission denied.
  185. * EMFILE - Too many file descriptors in use by process.
  186. * ENFILE - Too many files are currently open in the
  187. * system.
  188. * ENOENT - Directory does not exist, or name is an empty
  189. * string.
  190. * ENOMEM - Insufficient memory to complete the operation.
  191. * ENOTDIR - 'path' is not a directory.
  192. *
  193. ****************************************************************************/
  194. FAR DIR *opendir(FAR const char *path)
  195. {
  196. FAR struct inode *inode = NULL;
  197. FAR struct fs_dirent_s *dir;
  198. struct inode_search_s desc;
  199. #ifndef CONFIG_DISABLE_MOUNTPOINT
  200. FAR const char *relpath = NULL;
  201. #endif
  202. bool isroot = false;
  203. int ret;
  204. /* If we are given 'nothing' then we will interpret this as
  205. * request for the root inode.
  206. */
  207. SETUP_SEARCH(&desc, path, false);
  208. inode_semtake();
  209. if (path == NULL || *path == '\0' || strcmp(path, "/") == 0)
  210. {
  211. inode = g_root_inode;
  212. isroot = true;
  213. }
  214. else
  215. {
  216. /* We don't know what to do with relative pathes */
  217. if (*path != '/')
  218. {
  219. ret = -ENOTDIR;
  220. goto errout_with_semaphore;
  221. }
  222. /* Find the node matching the path. */
  223. ret = inode_search(&desc);
  224. if (ret >= 0)
  225. {
  226. inode = desc.node;
  227. DEBUGASSERT(inode != NULL);
  228. #ifndef CONFIG_DISABLE_MOUNTPOINT
  229. relpath = desc.relpath;
  230. #endif
  231. }
  232. }
  233. /* Did we get an inode? */
  234. if (inode == NULL)
  235. {
  236. /* Inode for 'path' does not exist. */
  237. ret = ENOTDIR;
  238. goto errout_with_semaphore;
  239. }
  240. /* Allocate a type DIR -- which is little more than an inode
  241. * container.
  242. */
  243. dir = (FAR struct fs_dirent_s *)kumm_zalloc(sizeof(struct fs_dirent_s));
  244. if (!dir)
  245. {
  246. /* Insufficient memory to complete the operation. */
  247. ret = ENOMEM;
  248. goto errout_with_semaphore;
  249. }
  250. /* Populate the DIR structure and return it to the caller. The way that
  251. * we do this depends on whenever this is a "normal" pseudo-file-system
  252. * inode or a file system mountpoint.
  253. */
  254. dir->fd_position = 0; /* This is the position in the read stream */
  255. /* First, handle the special case of the root inode. This must be
  256. * special-cased here because the root inode might ALSO be a mountpoint.
  257. */
  258. if (isroot)
  259. {
  260. /* Whatever payload the root inode carries, the root inode is always
  261. * a directory inode in the pseudo-file system
  262. */
  263. open_pseudodir(inode, dir);
  264. }
  265. /* Is this a node in the pseudo filesystem? Or a mountpoint? If the node
  266. * is the root (isroot == TRUE), then this is a special case.
  267. */
  268. #ifndef CONFIG_DISABLE_MOUNTPOINT
  269. else if (INODE_IS_MOUNTPT(inode))
  270. {
  271. /* Yes, the node is a file system mountpoint */
  272. dir->fd_root = inode; /* Save the inode where we start */
  273. /* Open the directory at the relative path */
  274. ret = open_mountpoint(inode, relpath, dir);
  275. if (ret != OK)
  276. {
  277. goto errout_with_direntry;
  278. }
  279. }
  280. #endif
  281. else
  282. {
  283. /* The node is part of the root pseudo file system. Does the inode
  284. * have a child? If so that the child would be the 'root' of a list
  285. * of nodes under the directory.
  286. */
  287. FAR struct inode *child = inode->i_child;
  288. if (child != NULL)
  289. {
  290. /* It looks we have a valid pseudo-filesystem directory node. */
  291. open_pseudodir(child, dir);
  292. }
  293. else if (!inode->u.i_ops)
  294. {
  295. /* This is a dangling node with no children and no operations. Set
  296. * up to enumerate an empty directory.
  297. */
  298. open_emptydir(dir);
  299. }
  300. else
  301. {
  302. ret = ENOTDIR;
  303. goto errout_with_direntry;
  304. }
  305. }
  306. RELEASE_SEARCH(&desc);
  307. inode_semgive();
  308. return ((FAR DIR *)dir);
  309. /* Nasty goto's make error handling simpler */
  310. errout_with_direntry:
  311. kumm_free(dir);
  312. errout_with_semaphore:
  313. RELEASE_SEARCH(&desc);
  314. inode_semgive();
  315. set_errno(ret);
  316. return NULL;
  317. }