xref: /third_party/NuttX/fs/dirent/fs_readdir.c (revision beacf11b)
1/****************************************************************************
2 * fs/dirent/fs_readdir.c
3 *
4 * Copyright (c) 2023 Huawei Device Co., Ltd. All rights reserved.
5 * Based on NuttX originally from nuttx source (nuttx/fs/ and nuttx/drivers/)
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * 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,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ****************************************************************************/
20
21/****************************************************************************
22 * Included Files
23 ****************************************************************************/
24
25#include "vfs_config.h"
26#include "string.h"
27#include "dirent.h"
28#include "errno.h"
29#include "unistd.h"
30#include "fs/dirent_fs.h"
31#include "user_copy.h"
32#include "fs/file.h"
33#include "vnode.h"
34
35/****************************************************************************
36 * Name: do_readdir
37 *
38 * Description:
39 *   The do_readdir() function returns a pointer to a dirent structure
40 *   representing the next directory entry in the directory stream pointed
41 *   to by dir.  It returns NULL on reaching the end-of-file or if an error
42 *   occurred.
43 *
44 * Input Parameters:
45 *   dirp -- An instance of type DIR created by a previous call to opendir();
46 *
47 * Returned Value:
48 *   The do_readdir() function returns a pointer to a dirent structure, or NULL
49 *   if an error occurs or end-of-file is reached.  On error, errno is set
50 *   appropriately.
51 *
52 *   EBADF   - Invalid directory stream descriptor dir
53 *
54 ****************************************************************************/
55static struct dirent *__readdir(DIR *dirp, int *lencnt)
56{
57  struct fs_dirent_s *idir = (struct fs_dirent_s *)dirp;
58  struct Vnode *vnode_ptr = NULL;
59  int ret = 0;
60  int file_cnt = 0;
61
62  /* Verify that we were provided with a valid directory structure */
63
64  if (!idir)
65    {
66      ret = -EBADF;
67      goto errout;
68    }
69
70  vnode_ptr = idir->fd_root;
71  if (vnode_ptr == NULL)
72    {
73      /* End of file and error conditions are not distinguishable
74       * with readdir.  We return NULL to signal either case.
75       */
76
77      ret = -ENOENT;
78      goto errout;
79    }
80
81  /* Perform the readdir() operation */
82#ifdef LOSCFG_ENABLE_READ_BUFFER
83  idir->read_cnt = MAX_DIRENT_NUM;
84#else
85  idir->read_cnt = 1;
86#endif
87
88  if (vnode_ptr->vop != NULL && vnode_ptr->vop->Readdir != NULL)
89    {
90      file_cnt = vnode_ptr->vop->Readdir(vnode_ptr, idir);
91    }
92  else
93    {
94      ret = -ENOSYS;
95      goto errout;
96    }
97
98  if (file_cnt > 0)
99    {
100      *lencnt = file_cnt * sizeof(struct dirent);
101      return &(idir->fd_dir[0]);
102    }
103
104errout:
105  if (ret < 0)
106    {
107      set_errno(ret);
108    }
109  else if (file_cnt <= 0)
110    {
111      set_errno(ENOENT);
112    }
113
114  return (struct dirent *)NULL;
115}
116
117/****************************************************************************
118 * Public Functions
119 ****************************************************************************/
120
121/****************************************************************************
122 * Name: readdir
123 *
124 * Description:
125 *   The readdir() function returns a pointer to a dirent structure
126 *   representing the next directory entry in the directory stream pointed
127 *   to by dir.  It returns NULL on reaching the end-of-file or if an error
128 *   occurred.
129 *
130 * Inputs:
131 *   dirp -- An instance of type DIR created by a previous call to opendir();
132 *
133 * Return:
134 *   The readdir() function returns a pointer to a dirent structure, or NULL
135 *   if an error occurs or end-of-file is reached.  On error, errno is set
136 *   appropriately.
137 *
138 *   EBADF   - Invalid directory stream descriptor dir
139 *
140 ****************************************************************************/
141struct dirent *readdir(DIR *dirp)
142{
143  int ret;
144  int old_err = get_errno();
145  int dirent_len = 0;
146  struct dirent *de = NULL;
147  struct fs_dirent_s *idir = (struct fs_dirent_s *)dirp;
148
149  if (idir->cur_pos != 0 && idir->cur_pos < MAX_DIRENT_NUM && idir->cur_pos < idir->end_pos)
150    {
151      de = &(idir->fd_dir[idir->cur_pos]);
152
153      if (idir->cur_pos == MAX_DIRENT_NUM)
154        {
155          idir->cur_pos = 0;
156        }
157      idir->cur_pos++;
158
159      return de;
160    }
161  else
162    {
163      de = __readdir(dirp, &dirent_len);
164      idir->end_pos = dirent_len / sizeof(struct dirent);
165      idir->cur_pos = 1;
166
167      if (de == NULL)
168        {
169          idir->cur_pos = 0;
170          ret = get_errno();
171          /* Special case: ret = -ENOENT is end of file */
172
173          if (ret == ENOENT)
174            {
175              set_errno(old_err);
176            }
177        }
178    }
179  return de;
180}
181
182/* readdir syscall routine */
183
184int do_readdir(int fd, struct dirent **de, unsigned int count)
185{
186  struct dirent *de_src = NULL;
187  int de_len = 0;
188  /* Did we get a valid file descriptor? */
189
190#if CONFIG_NFILE_DESCRIPTORS > 0
191  struct file *filep = NULL;
192
193  if (de == NULL)
194    {
195      return -EINVAL;
196    }
197
198  if ((fd < 3) || (unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS)
199    {
200      return -EBADF;
201    }
202  else
203    {
204      /* The descriptor is in a valid range to file descriptor... do the
205       * read.  First, get the file structure.
206       */
207
208      int ret = fs_getfilep(fd, &filep);
209      if (ret < 0)
210        {
211          /* The errno value has already been set */
212          return -get_errno();
213        }
214
215      /* Then let do_readdir do all of the work */
216
217      de_src = __readdir(filep->f_dir, &de_len);
218      if (de_src == NULL)
219        {
220          /* Special case: ret = -ENOENT is end of file */
221          return -get_errno();
222        }
223      *de = de_src;
224
225      return de_len;
226    }
227#endif
228  return OK;
229}
230