1 /* Read a directory in reentrant mode.  Linux LFS version.
2    Copyright (C) 1997-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* When _DIRENT_MATCHES_DIRENT64 is defined we can alias 'readdir64' to
20    'readdir'.  However the function signatures are not equal due
21    different return types, so we need to suppress {__}readdir so weak
22    and strong alias do not throw conflicting types errors.  */
23 #define readdir_r   __no_readdir_r_decl
24 #define __readdir_r __no___readdir_r_decl
25 #include <dirent.h>
26 #undef __readdir_r
27 #undef readdir_r
28 
29 /* Read a directory entry from DIRP.  */
30 int
__readdir64_r(DIR * dirp,struct dirent64 * entry,struct dirent64 ** result)31 __readdir64_r (DIR *dirp, struct dirent64 *entry, struct dirent64 **result)
32 {
33   struct dirent64 *dp;
34   size_t reclen;
35   const int saved_errno = errno;
36   int ret;
37 
38   __libc_lock_lock (dirp->lock);
39 
40   do
41     {
42       if (dirp->offset >= dirp->size)
43 	{
44 	  /* We've emptied out our buffer.  Refill it.  */
45 
46 	  size_t maxread = dirp->allocation;
47 	  ssize_t bytes;
48 
49 	  maxread = dirp->allocation;
50 
51 	  bytes = __getdents64 (dirp->fd, dirp->data, maxread);
52 	  if (bytes <= 0)
53 	    {
54 	      /* On some systems getdents fails with ENOENT when the
55 		 open directory has been rmdir'd already.  POSIX.1
56 		 requires that we treat this condition like normal EOF.  */
57 	      if (bytes < 0 && errno == ENOENT)
58 		{
59 		  bytes = 0;
60 		  __set_errno (saved_errno);
61 		}
62 	      if (bytes < 0)
63 		dirp->errcode = errno;
64 
65 	      dp = NULL;
66 	      break;
67 	    }
68 	  dirp->size = (size_t) bytes;
69 
70 	  /* Reset the offset into the buffer.  */
71 	  dirp->offset = 0;
72 	}
73 
74       dp = (struct dirent64 *) &dirp->data[dirp->offset];
75 
76       reclen = dp->d_reclen;
77 
78       dirp->offset += reclen;
79 
80       dirp->filepos = dp->d_off;
81 
82       if (reclen > offsetof (struct dirent64, d_name) + NAME_MAX + 1)
83 	{
84 	  /* The record is very long.  It could still fit into the
85 	     caller-supplied buffer if we can skip padding at the
86 	     end.  */
87 	  size_t namelen = _D_EXACT_NAMLEN (dp);
88 	  if (namelen <= NAME_MAX)
89 	    reclen = offsetof (struct dirent64, d_name) + namelen + 1;
90 	  else
91 	    {
92 	      /* The name is too long.  Ignore this file.  */
93 	      dirp->errcode = ENAMETOOLONG;
94 	      dp->d_ino = 0;
95 	      continue;
96 	    }
97 	}
98 
99       /* Skip deleted and ignored files.  */
100     }
101   while (dp->d_ino == 0);
102 
103   if (dp != NULL)
104     {
105       *result = memcpy (entry, dp, reclen);
106       entry->d_reclen = reclen;
107       ret = 0;
108     }
109   else
110     {
111       *result = NULL;
112       ret = dirp->errcode;
113     }
114 
115   __libc_lock_unlock (dirp->lock);
116 
117   return ret;
118 }
119 
120 
121 #if _DIRENT_MATCHES_DIRENT64
122 strong_alias (__readdir64_r, __readdir_r)
123 weak_alias (__readdir64_r, readdir_r)
124 weak_alias (__readdir64_r, readdir64_r)
125 #else
126 /* The compat code expects the 'struct direct' with d_ino being a __ino_t
127    instead of __ino64_t.  */
128 # include <shlib-compat.h>
129 versioned_symbol (libc, __readdir64_r, readdir64_r, GLIBC_2_2);
130 # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)
131 #  include <olddirent.h>
132 
133 int
134 attribute_compat_text_section
135 __old_readdir64_r (DIR *dirp, struct __old_dirent64 *entry,
136 		   struct __old_dirent64 **result)
137 {
138   struct __old_dirent64 *dp;
139   size_t reclen;
140   const int saved_errno = errno;
141   int ret;
142 
143   __libc_lock_lock (dirp->lock);
144 
145   do
146     {
147       if (dirp->offset >= dirp->size)
148 	{
149 	  /* We've emptied out our buffer.  Refill it.  */
150 
151 	  size_t maxread = dirp->allocation;
152 	  ssize_t bytes;
153 
154 	  maxread = dirp->allocation;
155 
156 	  bytes = __old_getdents64 (dirp->fd, dirp->data, maxread);
157 	  if (bytes <= 0)
158 	    {
159 	      /* On some systems getdents fails with ENOENT when the
160 		 open directory has been rmdir'd already.  POSIX.1
161 		 requires that we treat this condition like normal EOF.  */
162 	      if (bytes < 0 && errno == ENOENT)
163 		{
164 		  bytes = 0;
165 		  __set_errno (saved_errno);
166 		}
167 	      if (bytes < 0)
168 		dirp->errcode = errno;
169 
170 	      dp = NULL;
171 	      break;
172 	    }
173 	  dirp->size = (size_t) bytes;
174 
175 	  /* Reset the offset into the buffer.  */
176 	  dirp->offset = 0;
177 	}
178 
179       dp = (struct __old_dirent64 *) &dirp->data[dirp->offset];
180 
181       reclen = dp->d_reclen;
182 
183       dirp->offset += reclen;
184 
185       dirp->filepos = dp->d_off;
186 
187       if (reclen > offsetof (struct __old_dirent64, d_name) + NAME_MAX + 1)
188 	{
189 	  /* The record is very long.  It could still fit into the
190 	     caller-supplied buffer if we can skip padding at the
191 	     end.  */
192 	  size_t namelen = _D_EXACT_NAMLEN (dp);
193 	  if (namelen <= NAME_MAX)
194 	    reclen = offsetof (struct __old_dirent64, d_name) + namelen + 1;
195 	  else
196 	    {
197 	      /* The name is too long.  Ignore this file.  */
198 	      dirp->errcode = ENAMETOOLONG;
199 	      dp->d_ino = 0;
200 	      continue;
201 	    }
202 	}
203 
204       /* Skip deleted and ignored files.  */
205     }
206   while (dp->d_ino == 0);
207 
208   if (dp != NULL)
209     {
210       *result = memcpy (entry, dp, reclen);
211       entry->d_reclen = reclen;
212       ret = 0;
213     }
214   else
215     {
216       *result = NULL;
217       ret = dirp->errcode;
218     }
219 
220   __libc_lock_unlock (dirp->lock);
221 
222   return ret;
223 }
224 
225 compat_symbol (libc, __old_readdir64_r, readdir64_r, GLIBC_2_1);
226 # endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2)  */
227 #endif /* _DIRENT_MATCHES_DIRENT64  */
228