xref: /third_party/NuttX/fs/dirent/fs_opendir.c (revision beacf11b)
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
84DIR *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
145errout_with_count:
146  VnodeHold();
147  vp->useCount--;
148  VnodeDrop();
149errout:
150  set_errno(-ret);
151  return NULL;
152}
153
154int 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
245errout_with_file:
246  files_release(filep->fd);
247errout_with_vnode:
248  VnodeHold();
249  vp->useCount--;
250  VnodeDrop();
251errout:
252  if (fullpath)
253    {
254      free(fullpath);
255    }
256  set_errno(-ret);
257  return VFS_ERROR;
258}
259