1*144bb499Skre /* $NetBSD: initdir.c,v 1.5 2021/07/11 16:30:41 kre Exp $ */
2ab5972b0Syamt
3ab5972b0Syamt /*
4ab5972b0Syamt * Copyright (c) 1983, 1993
5ab5972b0Syamt * The Regents of the University of California. All rights reserved.
6ab5972b0Syamt *
7ab5972b0Syamt * Redistribution and use in source and binary forms, with or without
8ab5972b0Syamt * modification, are permitted provided that the following conditions
9ab5972b0Syamt * are met:
10ab5972b0Syamt * 1. Redistributions of source code must retain the above copyright
11ab5972b0Syamt * notice, this list of conditions and the following disclaimer.
12ab5972b0Syamt * 2. Redistributions in binary form must reproduce the above copyright
13ab5972b0Syamt * notice, this list of conditions and the following disclaimer in the
14ab5972b0Syamt * documentation and/or other materials provided with the distribution.
15ab5972b0Syamt * 3. Neither the name of the University nor the names of its contributors
16ab5972b0Syamt * may be used to endorse or promote products derived from this software
17ab5972b0Syamt * without specific prior written permission.
18ab5972b0Syamt *
19ab5972b0Syamt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20ab5972b0Syamt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21ab5972b0Syamt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22ab5972b0Syamt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23ab5972b0Syamt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24ab5972b0Syamt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25ab5972b0Syamt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26ab5972b0Syamt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27ab5972b0Syamt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28ab5972b0Syamt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29ab5972b0Syamt * SUCH DAMAGE.
30ab5972b0Syamt */
31ab5972b0Syamt
32ab5972b0Syamt #include <sys/cdefs.h>
33ab5972b0Syamt #if defined(LIBC_SCCS) && !defined(lint)
34*144bb499Skre __RCSID("$NetBSD: initdir.c,v 1.5 2021/07/11 16:30:41 kre Exp $");
35ab5972b0Syamt #endif /* LIBC_SCCS and not lint */
36ab5972b0Syamt
37ab5972b0Syamt #include "namespace.h"
38ab5972b0Syamt #include "reentrant.h"
39ab5972b0Syamt #include "extern.h"
40ab5972b0Syamt
41ab5972b0Syamt #include <sys/param.h>
42ab5972b0Syamt
43ab5972b0Syamt #include <assert.h>
44ab5972b0Syamt #include <dirent.h>
45ab5972b0Syamt #include <errno.h>
46ab5972b0Syamt #include <fcntl.h>
47ab5972b0Syamt #include <stdlib.h>
48ab5972b0Syamt #include <string.h>
49ab5972b0Syamt #include <unistd.h>
50ab5972b0Syamt
51ab5972b0Syamt #include "dirent_private.h"
52ab5972b0Syamt
53ab5972b0Syamt #define MAXITERATIONS 100
54ab5972b0Syamt
55ab5972b0Syamt int
_initdir(DIR * dirp,int fd,const char * name)56ab5972b0Syamt _initdir(DIR *dirp, int fd, const char *name)
57ab5972b0Syamt {
58ab5972b0Syamt int flags = dirp->dd_flags;
59ab5972b0Syamt int pagesz;
60ab5972b0Syamt int incr;
61ab5972b0Syamt
62ab5972b0Syamt /*
63ab5972b0Syamt * If the machine's page size is an exact multiple of DIRBLKSIZ,
64ab5972b0Syamt * use a buffer that is cluster boundary aligned.
65ab5972b0Syamt * Hopefully this can be a big win someday by allowing page trades
66ab5972b0Syamt * to user space to be done by getdents()
67ab5972b0Syamt */
68ab5972b0Syamt if (((pagesz = getpagesize()) % DIRBLKSIZ) == 0)
69ab5972b0Syamt incr = pagesz;
70ab5972b0Syamt else
71ab5972b0Syamt incr = DIRBLKSIZ;
72ab5972b0Syamt
73ab5972b0Syamt if ((flags & DTF_REWIND) && name == NULL) {
74ab5972b0Syamt return EINVAL;
75ab5972b0Syamt }
76ab5972b0Syamt if ((flags & __DTF_READALL) != 0) {
77ab5972b0Syamt size_t len;
78ab5972b0Syamt size_t space;
79ab5972b0Syamt char *buf, *nbuf;
80ab5972b0Syamt char *ddptr;
81ab5972b0Syamt char *ddeptr;
82ab5972b0Syamt int n;
83ab5972b0Syamt struct dirent **dpv;
84ab5972b0Syamt int i;
85ab5972b0Syamt
86ab5972b0Syamt /*
87ab5972b0Syamt * The strategy here for directories on top of a union stack
88ab5972b0Syamt * is to read all the directory entries into a buffer, sort
89ab5972b0Syamt * the buffer, and remove duplicate entries by setting the
90ab5972b0Syamt * inode number to zero.
91ab5972b0Syamt *
92ab5972b0Syamt * For directories on an NFS mounted filesystem, we try
93ab5972b0Syamt * to get a consistent snapshot by trying until we have
94ab5972b0Syamt * successfully read all of the directory without errors
95ab5972b0Syamt * (i.e. 'bad cookie' errors from the server because
96ab5972b0Syamt * the directory was modified). These errors should not
97ab5972b0Syamt * happen often, but need to be dealt with.
98ab5972b0Syamt */
99ab5972b0Syamt i = 0;
100ab5972b0Syamt retry:
101ab5972b0Syamt len = 0;
102ab5972b0Syamt space = 0;
103ab5972b0Syamt buf = 0;
104ab5972b0Syamt ddptr = 0;
105ab5972b0Syamt
106ab5972b0Syamt do {
107ab5972b0Syamt /*
108ab5972b0Syamt * Always make at least DIRBLKSIZ bytes
109ab5972b0Syamt * available to getdents
110ab5972b0Syamt */
111ab5972b0Syamt if (space < DIRBLKSIZ) {
112ab5972b0Syamt space += incr;
113ab5972b0Syamt len += incr;
114ab5972b0Syamt nbuf = realloc(buf, len);
115ab5972b0Syamt if (nbuf == NULL) {
116ab5972b0Syamt dirp->dd_buf = buf;
117ab5972b0Syamt return errno;
118ab5972b0Syamt }
119ab5972b0Syamt buf = nbuf;
120ab5972b0Syamt ddptr = buf + (len - space);
121ab5972b0Syamt }
122ab5972b0Syamt
123ab5972b0Syamt dirp->dd_seek = lseek(fd, (off_t)0, SEEK_CUR);
124ab5972b0Syamt n = getdents(fd, ddptr, space);
125ab5972b0Syamt /*
126ab5972b0Syamt * For NFS: EINVAL means a bad cookie error
127ab5972b0Syamt * from the server. Keep trying to get a
128ab5972b0Syamt * consistent view, in this case this means
129ab5972b0Syamt * starting all over again.
130ab5972b0Syamt */
131ab5972b0Syamt if (n == -1 && errno == EINVAL &&
132ab5972b0Syamt (flags & __DTF_RETRY_ON_BADCOOKIE) != 0) {
133ab5972b0Syamt free(buf);
134ab5972b0Syamt lseek(fd, (off_t)0, SEEK_SET);
135ab5972b0Syamt if (++i > MAXITERATIONS)
136ab5972b0Syamt return EINVAL;
137ab5972b0Syamt goto retry;
138ab5972b0Syamt }
139ab5972b0Syamt if (n > 0) {
140ab5972b0Syamt ddptr += n;
141ab5972b0Syamt space -= n;
142ab5972b0Syamt }
143ab5972b0Syamt } while (n > 0);
144ab5972b0Syamt
145ab5972b0Syamt ddeptr = ddptr;
146ab5972b0Syamt
147ab5972b0Syamt /*
148ab5972b0Syamt * Re-open the directory.
149ab5972b0Syamt * This has the effect of rewinding back to the
150ab5972b0Syamt * top of the union stack and is needed by
151ab5972b0Syamt * programs which plan to fchdir to a descriptor
152ab5972b0Syamt * which has also been read -- see fts.c.
153ab5972b0Syamt */
154ab5972b0Syamt if (flags & DTF_REWIND) {
155ab5972b0Syamt (void) close(fd);
1569292cfb2Schristos if ((fd = open(name, O_RDONLY | O_CLOEXEC)) == -1) {
157ab5972b0Syamt dirp->dd_buf = buf;
158ab5972b0Syamt return errno;
159ab5972b0Syamt }
160ab5972b0Syamt }
161ab5972b0Syamt
162ab5972b0Syamt /*
163ab5972b0Syamt * There is now a buffer full of (possibly) duplicate
164ab5972b0Syamt * names.
165ab5972b0Syamt */
166ab5972b0Syamt dirp->dd_buf = buf;
167ab5972b0Syamt
168ab5972b0Syamt /*
169ab5972b0Syamt * Go round this loop twice...
170ab5972b0Syamt *
171ab5972b0Syamt * Scan through the buffer, counting entries.
172ab5972b0Syamt * On the second pass, save pointers to each one.
173ab5972b0Syamt * Then sort the pointers and remove duplicate names.
174ab5972b0Syamt */
175ab5972b0Syamt if ((flags & DTF_NODUP) != 0) {
176ab5972b0Syamt for (dpv = 0;;) {
177ab5972b0Syamt for (n = 0, ddptr = buf; ddptr < ddeptr;) {
178ab5972b0Syamt struct dirent *dp;
179ab5972b0Syamt
180ab5972b0Syamt dp = (struct dirent *)(void *)ddptr;
181ab5972b0Syamt if ((long)dp & _DIRENT_ALIGN(dp))
182ab5972b0Syamt break;
183ab5972b0Syamt /*
184ab5972b0Syamt * d_reclen is unsigned,
185ab5972b0Syamt * so no need to compare <= 0
186ab5972b0Syamt */
187ab5972b0Syamt if (dp->d_reclen > (ddeptr + 1 - ddptr))
188ab5972b0Syamt break;
189ab5972b0Syamt ddptr += dp->d_reclen;
190ab5972b0Syamt if (dp->d_fileno) {
191ab5972b0Syamt if (dpv)
192ab5972b0Syamt dpv[n] = dp;
193ab5972b0Syamt n++;
194ab5972b0Syamt }
195ab5972b0Syamt }
196ab5972b0Syamt
197ab5972b0Syamt if (dpv) {
198ab5972b0Syamt struct dirent *xp;
199ab5972b0Syamt
200ab5972b0Syamt /*
201ab5972b0Syamt * This sort must be stable.
202ab5972b0Syamt */
203ab5972b0Syamt mergesort(dpv, (size_t)n, sizeof(*dpv),
204f1582e88Smrg (int (*)(const void *,
205f1582e88Smrg const void *))alphasort);
206ab5972b0Syamt
207ab5972b0Syamt dpv[n] = NULL;
208ab5972b0Syamt xp = NULL;
209ab5972b0Syamt
210ab5972b0Syamt /*
211ab5972b0Syamt * Scan through the buffer in sort
212ab5972b0Syamt * order, zapping the inode number
213ab5972b0Syamt * of any duplicate names.
214ab5972b0Syamt */
215ab5972b0Syamt for (n = 0; dpv[n]; n++) {
216ab5972b0Syamt struct dirent *dp = dpv[n];
217ab5972b0Syamt
218ab5972b0Syamt if ((xp == NULL) ||
219ab5972b0Syamt strcmp(dp->d_name,
220ab5972b0Syamt xp->d_name))
221ab5972b0Syamt xp = dp;
222ab5972b0Syamt else
223ab5972b0Syamt dp->d_fileno = 0;
224ab5972b0Syamt if (dp->d_type == DT_WHT &&
225ab5972b0Syamt (flags & DTF_HIDEW))
226ab5972b0Syamt dp->d_fileno = 0;
227ab5972b0Syamt }
228ab5972b0Syamt
229ab5972b0Syamt free(dpv);
230ab5972b0Syamt break;
231ab5972b0Syamt } else {
232ab5972b0Syamt dpv = malloc((n + 1) *
233ab5972b0Syamt sizeof(struct dirent *));
234ab5972b0Syamt if (dpv == NULL)
235ab5972b0Syamt break;
236ab5972b0Syamt }
237ab5972b0Syamt }
238ab5972b0Syamt }
239ab5972b0Syamt
240c5e820caSchristos _DIAGASSERT(__type_fit(int, len));
241c5e820caSchristos dirp->dd_len = (int)len;
242ab5972b0Syamt dirp->dd_size = ddptr - dirp->dd_buf;
243ab5972b0Syamt } else {
244ab5972b0Syamt dirp->dd_len = incr;
245*144bb499Skre dirp->dd_size = 0;
246ab5972b0Syamt dirp->dd_buf = malloc((size_t)dirp->dd_len);
247ab5972b0Syamt if (dirp->dd_buf == NULL)
248ab5972b0Syamt return errno;
249ab5972b0Syamt dirp->dd_seek = 0;
250ab5972b0Syamt flags &= ~DTF_REWIND;
251ab5972b0Syamt }
252ab5972b0Syamt dirp->dd_loc = 0;
253ab5972b0Syamt dirp->dd_fd = fd;
254ab5972b0Syamt dirp->dd_flags = flags;
255ab5972b0Syamt /*
256ab5972b0Syamt * Set up seek point for rewinddir.
257ab5972b0Syamt */
258ab5972b0Syamt (void)_telldir_unlocked(dirp);
259ab5972b0Syamt return 0;
260ab5972b0Syamt }
261ab5972b0Syamt
262ab5972b0Syamt void
_finidir(DIR * dirp)263ab5972b0Syamt _finidir(DIR *dirp)
264ab5972b0Syamt {
265ab5972b0Syamt struct dirpos *poslist;
266ab5972b0Syamt
267ab5972b0Syamt free(dirp->dd_buf);
268ab5972b0Syamt
269ab5972b0Syamt /* free seekdir/telldir storage */
270ab5972b0Syamt for (poslist = dirp->dd_internal; poslist; ) {
271ab5972b0Syamt struct dirpos *nextpos = poslist->dp_next;
272ab5972b0Syamt free(poslist);
273ab5972b0Syamt poslist = nextpos;
274ab5972b0Syamt }
275ab5972b0Syamt dirp->dd_internal = NULL;
276ab5972b0Syamt }
277