1 /****************************************************************************
2 * fs/dirent/fs_opendir.c
3 *
4 * Copyright (C) 2007-2009, 2011, 2013-2014, 2017-2018 Gregory Nutt. All
5 * rights reserved.
6 * Author: Gregory Nutt <gnutt@nuttx.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * 3. Neither the name NuttX nor the names of its contributors may be
19 * used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 ****************************************************************************/
36
37 /****************************************************************************
38 * Included Files
39 ****************************************************************************/
40
41 #include "vfs_config.h"
42 #include "dirent.h"
43 #include "string.h"
44 #include "assert.h"
45 #include "errno.h"
46 #include "stdlib.h"
47 #include "fs/dirent_fs.h"
48 #include "vnode.h"
49 #include "path_cache.h"
50
51 /****************************************************************************
52 * Public Functions
53 ****************************************************************************/
54
55 /****************************************************************************
56 * Name: opendir
57 *
58 * Description:
59 * The opendir() function opens a directory stream corresponding to the
60 * directory name, and returns a pointer to the directory stream. The
61 * stream is positioned at the first entry in the directory.
62 *
63 * Input Parameters:
64 * path -- the directory to open
65 *
66 * Returned Value:
67 * The opendir() function returns a pointer to the directory stream. On
68 * error, NULL is returned, and errno is set appropriately.
69 *
70 * EACCES - Permission denied.
71 * EMFILE - Too many file descriptors in use by process.
72 * ENFILE - Too many files are currently open in the
73 * system.
74 * ENOENT - Directory does not exist, or name is an empty
75 * string.
76 * ENOMEM - Insufficient memory to complete the operation.
77 * ENOTDIR - 'path' is not a directory.
78 *
79 ****************************************************************************/
80
opendir(const char * path)81 DIR *opendir(const char *path)
82 {
83 struct Vnode *vp = NULL;
84 struct fs_dirent_s *dir = NULL;
85 int ret;
86
87 /* Find the node matching the path. */
88 VnodeHold();
89 ret = VnodeLookup(path, &vp, 0);
90 if (vp == NULL || ret != OK)
91 {
92 VnodeDrop();
93 goto errout;
94 }
95 if (vp->type != VNODE_TYPE_DIR)
96 {
97 ret = -ENOTDIR;
98 PRINT_ERR("opendir (%s) failed, err=%d\n", path, ret);
99 VnodeDrop();
100 goto errout;
101 }
102
103 vp->useCount++;
104 VnodeDrop();
105
106 /* Allocate a type DIR -- which is little more than an vp container. */
107 dir = (struct fs_dirent_s *)calloc(1, sizeof(struct fs_dirent_s));
108 if (!dir)
109 {
110 /* Insufficient memory to complete the operation. */
111 ret = -ENOMEM;
112 goto errout_with_count;
113 }
114
115 /* Populate the DIR structure and return it to the caller. The way that
116 * we do this depends on whenever this is a "normal" pseudo-file-system
117 * vp or a file system mountpoint.
118 */
119
120 dir->fd_position = 0; /* This is the position in the read stream */
121
122 if (vp->vop != NULL && vp->vop->Opendir != NULL)
123 {
124 ret = vp->vop->Opendir(vp, dir);
125 }
126 else
127 {
128 ret = -ENOSYS;
129 }
130 if (ret < 0)
131 {
132 free(dir);
133 goto errout_with_count;
134 }
135 dir->fd_status = DIRENT_MAGIC;
136 dir->fd_root = vp;
137
138 return ((DIR *)dir);
139
140 /* Nasty goto's make error handling simpler */
141
142 errout_with_count:
143 VnodeHold();
144 vp->useCount--;
145 VnodeDrop();
146 errout:
147 set_errno(-ret);
148 return NULL;
149 }
150
do_opendir(const char * path,int oflags)151 int do_opendir(const char *path, int oflags)
152 {
153 int ret;
154
155 struct Vnode *vp = NULL;
156 struct file *filep = NULL;
157 struct fs_dirent_s *dir = NULL;
158 char *fullpath = NULL;
159 char *relativepath = NULL;
160
161 if (path == NULL || *path == 0)
162 {
163 ret = -EINVAL;
164 goto errout;
165 }
166
167 ret = get_path_from_fd(AT_FDCWD, &relativepath);
168 if (ret < 0)
169 {
170 goto errout;
171 }
172
173 ret = vfs_normalize_path((const char *)relativepath, path, &fullpath);
174 if (relativepath)
175 {
176 free(relativepath);
177 }
178 if (ret < 0)
179 {
180 goto errout;
181 }
182
183 VnodeHold();
184 /* Get an vnode for this file */
185 ret = VnodeLookup(path, &vp, 0);
186 if (ret < 0)
187 {
188 VnodeDrop();
189 goto errout;
190 }
191 if (vp->type != VNODE_TYPE_DIR)
192 {
193 ret = -ENOTDIR;
194 VnodeDrop();
195 goto errout;
196 }
197 vp->useCount++;
198 VnodeDrop();
199
200 filep = files_allocate(vp, oflags, 0, NULL, FILE_START_FD);
201 if (filep == NULL)
202 {
203 ret = -EMFILE;
204 goto errout_with_vnode;
205 }
206
207 /* Allocate a type DIR -- which is little more than an vnode container. */
208 dir = (struct fs_dirent_s *)zalloc(sizeof(struct fs_dirent_s));
209 if (dir == NULL)
210 {
211 ret = -ENOMEM;
212 goto errout_with_file;
213 }
214 dir->fd_position = 0; /* This is the position in the read stream */
215
216 /* Open the directory at the relative path */
217 if (vp->vop != NULL && vp->vop->Opendir != NULL)
218 {
219 ret = vp->vop->Opendir(vp, dir);
220 }
221 else
222 {
223 ret = -ENOSYS;
224 }
225
226 if (ret < 0)
227 {
228 free(dir);
229 goto errout_with_file;
230 }
231
232 dir->fd_root = vp;
233 dir->fd_status = DIRENT_MAGIC;
234 filep->f_dir = dir;
235 if (fullpath)
236 {
237 free(fullpath);
238 }
239
240 return filep->fd;
241
242 errout_with_file:
243 files_release(filep->fd);
244 errout_with_vnode:
245 VnodeHold();
246 vp->useCount--;
247 VnodeDrop();
248 errout:
249 if (fullpath)
250 {
251 free(fullpath);
252 }
253 set_errno(-ret);
254 return VFS_ERROR;
255 }
256