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