1*a774e231Schristos /* $NetBSD: readdir.c,v 1.4 2015/01/18 16:37:05 christos Exp $ */
2a53f50b9Schristos
3a53f50b9Schristos /*
423de60b7Schristos * Copyright (c) 1997-2014 Erez Zadok
5a53f50b9Schristos * Copyright (c) 1990 Jan-Simon Pendry
6a53f50b9Schristos * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7a53f50b9Schristos * Copyright (c) 1990 The Regents of the University of California.
8a53f50b9Schristos * All rights reserved.
9a53f50b9Schristos *
10a53f50b9Schristos * This code is derived from software contributed to Berkeley by
11a53f50b9Schristos * Jan-Simon Pendry at Imperial College, London.
12a53f50b9Schristos *
13a53f50b9Schristos * Redistribution and use in source and binary forms, with or without
14a53f50b9Schristos * modification, are permitted provided that the following conditions
15a53f50b9Schristos * are met:
16a53f50b9Schristos * 1. Redistributions of source code must retain the above copyright
17a53f50b9Schristos * notice, this list of conditions and the following disclaimer.
18a53f50b9Schristos * 2. Redistributions in binary form must reproduce the above copyright
19a53f50b9Schristos * notice, this list of conditions and the following disclaimer in the
20a53f50b9Schristos * documentation and/or other materials provided with the distribution.
2123de60b7Schristos * 3. Neither the name of the University nor the names of its contributors
22a53f50b9Schristos * may be used to endorse or promote products derived from this software
23a53f50b9Schristos * without specific prior written permission.
24a53f50b9Schristos *
25a53f50b9Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26a53f50b9Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27a53f50b9Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28a53f50b9Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29a53f50b9Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30a53f50b9Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31a53f50b9Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32a53f50b9Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33a53f50b9Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34a53f50b9Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35a53f50b9Schristos * SUCH DAMAGE.
36a53f50b9Schristos *
37a53f50b9Schristos *
38a53f50b9Schristos * File: am-utils/amd/readdir.c
39a53f50b9Schristos *
40a53f50b9Schristos */
41a53f50b9Schristos
42a53f50b9Schristos
43a53f50b9Schristos #ifdef HAVE_CONFIG_H
44a53f50b9Schristos # include <config.h>
45a53f50b9Schristos #endif /* HAVE_CONFIG_H */
46a53f50b9Schristos #include <am_defs.h>
47a53f50b9Schristos #include <amd.h>
48a53f50b9Schristos
49a53f50b9Schristos
50a53f50b9Schristos /****************************************************************************
51a53f50b9Schristos *** MACROS ***
52a53f50b9Schristos ****************************************************************************/
53a53f50b9Schristos #define DOT_DOT_COOKIE (u_int) 1
54a53f50b9Schristos #define MAX_CHAIN 2048
55a53f50b9Schristos
56a53f50b9Schristos /****************************************************************************
57a53f50b9Schristos *** FORWARD DEFINITIONS ***
58a53f50b9Schristos ****************************************************************************/
59a53f50b9Schristos static int key_already_in_chain(char *keyname, const nfsentry *chain);
60a53f50b9Schristos static nfsentry *make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable);
61a53f50b9Schristos static int amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable);
62a53f50b9Schristos
6323de60b7Schristos static const u_int dotdotcookie = DOT_DOT_COOKIE;
64a53f50b9Schristos
65a53f50b9Schristos /****************************************************************************
66a53f50b9Schristos *** FUNCTIONS ***
67a53f50b9Schristos ****************************************************************************/
68a53f50b9Schristos /*
69a53f50b9Schristos * Was: NEW_TOPLVL_READDIR
70a53f50b9Schristos * Search a chain for an entry with some name.
71a53f50b9Schristos * -Erez Zadok <ezk@cs.columbia.edu>
72a53f50b9Schristos */
73a53f50b9Schristos static int
key_already_in_chain(char * keyname,const nfsentry * chain)74a53f50b9Schristos key_already_in_chain(char *keyname, const nfsentry *chain)
75a53f50b9Schristos {
76a53f50b9Schristos const nfsentry *tmpchain = chain;
77a53f50b9Schristos
78a53f50b9Schristos while (tmpchain) {
79a53f50b9Schristos if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name))
80a53f50b9Schristos return 1;
81a53f50b9Schristos tmpchain = tmpchain->ne_nextentry;
82a53f50b9Schristos }
83a53f50b9Schristos
84a53f50b9Schristos return 0;
85a53f50b9Schristos }
86a53f50b9Schristos
87a53f50b9Schristos
88a53f50b9Schristos /*
89a53f50b9Schristos * Create a chain of entries which are not linked.
90a53f50b9Schristos * -Erez Zadok <ezk@cs.columbia.edu>
91a53f50b9Schristos */
92a53f50b9Schristos static nfsentry *
make_entry_chain(am_node * mp,const nfsentry * current_chain,int fully_browsable)93a53f50b9Schristos make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable)
94a53f50b9Schristos {
95a53f50b9Schristos static u_int last_cookie = (u_int) 2; /* monotonically increasing */
96a53f50b9Schristos static nfsentry chain[MAX_CHAIN];
97a53f50b9Schristos static int max_entries = MAX_CHAIN;
98a53f50b9Schristos char *key;
99a53f50b9Schristos int num_entries = 0, i;
100a53f50b9Schristos u_int preflen = 0;
101a53f50b9Schristos nfsentry *retval = (nfsentry *) NULL;
102a53f50b9Schristos mntfs *mf;
103a53f50b9Schristos mnt_map *mmp;
104a53f50b9Schristos
105a53f50b9Schristos if (!mp) {
106a53f50b9Schristos plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)");
107a53f50b9Schristos return retval;
108a53f50b9Schristos }
10923de60b7Schristos mf = mp->am_al->al_mnt;
110a53f50b9Schristos if (!mf) {
11123de60b7Schristos plog(XLOG_DEBUG, "make_entry_chain: mp->am_al->al_mnt is (NULL)");
112a53f50b9Schristos return retval;
113a53f50b9Schristos }
114a53f50b9Schristos mmp = (mnt_map *) mf->mf_private;
115a53f50b9Schristos if (!mmp) {
11623de60b7Schristos plog(XLOG_DEBUG, "make_entry_chain: mp->am_al->al_mnt->mf_private is (NULL)");
117a53f50b9Schristos return retval;
118a53f50b9Schristos }
119a53f50b9Schristos
120a53f50b9Schristos if (mp->am_pref)
121a53f50b9Schristos preflen = strlen(mp->am_pref);
122a53f50b9Schristos
123a53f50b9Schristos /* iterate over keys */
124a53f50b9Schristos for (i = 0; i < NKVHASH; i++) {
125a53f50b9Schristos kv *k;
126a53f50b9Schristos for (k = mmp->kvhash[i]; k ; k = k->next) {
127a53f50b9Schristos
128a53f50b9Schristos /*
129a53f50b9Schristos * Skip unwanted entries which are either not real entries or
130a53f50b9Schristos * very difficult to interpret (wildcards...) This test needs
131a53f50b9Schristos * lots of improvement. Any takers?
132a53f50b9Schristos */
133a53f50b9Schristos key = k->key;
134a53f50b9Schristos if (!key)
135a53f50b9Schristos continue;
136a53f50b9Schristos
137a53f50b9Schristos /* Skip '/defaults' */
138a53f50b9Schristos if (STREQ(key, "/defaults"))
139a53f50b9Schristos continue;
140a53f50b9Schristos
141a53f50b9Schristos /* Skip '*' */
142a53f50b9Schristos if (!fully_browsable && strchr(key, '*'))
143a53f50b9Schristos continue;
144a53f50b9Schristos
145a53f50b9Schristos /*
146a53f50b9Schristos * If the map has a prefix-string then check if the key starts with
147a53f50b9Schristos * this string, and if it does, skip over this prefix. If it has a
148a53f50b9Schristos * prefix and it doesn't match the start of the key, skip it.
149a53f50b9Schristos */
150a53f50b9Schristos if (preflen) {
151a53f50b9Schristos if (preflen > strlen(key))
152a53f50b9Schristos continue;
153a53f50b9Schristos if (!NSTREQ(key, mp->am_pref, preflen))
154a53f50b9Schristos continue;
155a53f50b9Schristos key += preflen;
156a53f50b9Schristos }
157a53f50b9Schristos
158a53f50b9Schristos /* no more '/' are allowed, unless browsable_dirs=full was used */
159a53f50b9Schristos if (!fully_browsable && strchr(key, '/'))
160a53f50b9Schristos continue;
161a53f50b9Schristos
162a53f50b9Schristos /* no duplicates allowed */
163a53f50b9Schristos if (key_already_in_chain(key, current_chain))
164a53f50b9Schristos continue;
165a53f50b9Schristos
166a53f50b9Schristos /* fill in a cell and link the entry */
167a53f50b9Schristos if (num_entries >= max_entries) {
168a53f50b9Schristos /* out of space */
169a53f50b9Schristos plog(XLOG_DEBUG, "make_entry_chain: no more space in chain");
170a53f50b9Schristos if (num_entries > 0) {
171a53f50b9Schristos chain[num_entries - 1].ne_nextentry = NULL;
172a53f50b9Schristos retval = &chain[0];
173a53f50b9Schristos }
174a53f50b9Schristos return retval;
175a53f50b9Schristos }
176a53f50b9Schristos
177a53f50b9Schristos /* we have space. put entry in next cell */
178a53f50b9Schristos ++last_cookie;
17923de60b7Schristos chain[num_entries].ne_fileid = last_cookie;
18023de60b7Schristos (void)memcpy(chain[num_entries].ne_cookie, &last_cookie,
18123de60b7Schristos sizeof(last_cookie));
182a53f50b9Schristos chain[num_entries].ne_name = key;
183a53f50b9Schristos if (num_entries < max_entries - 1) { /* link to next one */
184a53f50b9Schristos chain[num_entries].ne_nextentry = &chain[num_entries + 1];
185a53f50b9Schristos }
186a53f50b9Schristos ++num_entries;
187a53f50b9Schristos } /* end of "while (k)" */
188a53f50b9Schristos } /* end of "for (i ... NKVHASH ..." */
189a53f50b9Schristos
190a53f50b9Schristos /* terminate chain */
191a53f50b9Schristos if (num_entries > 0) {
192a53f50b9Schristos chain[num_entries - 1].ne_nextentry = NULL;
193a53f50b9Schristos retval = &chain[0];
194a53f50b9Schristos }
195a53f50b9Schristos
196a53f50b9Schristos return retval;
197a53f50b9Schristos }
198a53f50b9Schristos
199a53f50b9Schristos
200a53f50b9Schristos
201a53f50b9Schristos /* This one is called only if map is browsable */
202a53f50b9Schristos static int
amfs_readdir_browsable(am_node * mp,nfscookie cookie,nfsdirlist * dp,nfsentry * ep,u_int count,int fully_browsable)203a53f50b9Schristos amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable)
204a53f50b9Schristos {
205a53f50b9Schristos u_int gen = *(u_int *) cookie;
206a53f50b9Schristos int chain_length, i;
207a53f50b9Schristos static nfsentry *te, *te_next;
208a53f50b9Schristos static int j;
209a53f50b9Schristos
210a53f50b9Schristos dp->dl_eof = FALSE; /* assume readdir not done */
211a53f50b9Schristos
212a53f50b9Schristos if (amuDebug(D_READDIR))
213a53f50b9Schristos plog(XLOG_DEBUG, "amfs_readdir_browsable gen=%u, count=%d",
214a53f50b9Schristos gen, count);
215a53f50b9Schristos
216a53f50b9Schristos if (gen == 0) {
217a53f50b9Schristos /*
218a53f50b9Schristos * In the default instance (which is used to start a search) we return
219a53f50b9Schristos * "." and "..".
220a53f50b9Schristos *
221a53f50b9Schristos * This assumes that the count is big enough to allow both "." and ".."
222a53f50b9Schristos * to be returned in a single packet. If it isn't (which would be
223a53f50b9Schristos * fairly unbelievable) then tough.
224a53f50b9Schristos */
22523de60b7Schristos dlog("%s: default search", __func__);
226a53f50b9Schristos /*
227a53f50b9Schristos * Check for enough room. This is extremely approximate but is more
228a53f50b9Schristos * than enough space. Really need 2 times:
229a53f50b9Schristos * 4byte fileid
230a53f50b9Schristos * 4byte cookie
231a53f50b9Schristos * 4byte name length
232a53f50b9Schristos * 4byte name
233a53f50b9Schristos * plus the dirlist structure */
234a53f50b9Schristos if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
235a53f50b9Schristos return EINVAL;
236a53f50b9Schristos
237a53f50b9Schristos /*
238a53f50b9Schristos * compute # of entries to send in this chain.
239a53f50b9Schristos * heuristics: 128 bytes per entry.
240a53f50b9Schristos * This is too much probably, but it seems to work better because
241a53f50b9Schristos * of the re-entrant nature of nfs_readdir, and esp. on systems
242a53f50b9Schristos * like OpenBSD 2.2.
243a53f50b9Schristos */
244a53f50b9Schristos chain_length = count / 128;
245a53f50b9Schristos
246a53f50b9Schristos /* reset static state counters */
247a53f50b9Schristos te = te_next = NULL;
248a53f50b9Schristos
249a53f50b9Schristos dp->dl_entries = ep;
250a53f50b9Schristos
251a53f50b9Schristos /* construct "." */
252a53f50b9Schristos ep[0].ne_fileid = mp->am_gen;
253a53f50b9Schristos ep[0].ne_name = ".";
254a53f50b9Schristos ep[0].ne_nextentry = &ep[1];
25523de60b7Schristos (void)memset(ep[0].ne_cookie, 0, sizeof(u_int));
256a53f50b9Schristos
257a53f50b9Schristos /* construct ".." */
258a53f50b9Schristos if (mp->am_parent)
259a53f50b9Schristos ep[1].ne_fileid = mp->am_parent->am_gen;
260a53f50b9Schristos else
261a53f50b9Schristos ep[1].ne_fileid = mp->am_gen;
262a53f50b9Schristos
263a53f50b9Schristos ep[1].ne_name = "..";
264a53f50b9Schristos ep[1].ne_nextentry = NULL;
26523de60b7Schristos (void)memcpy(ep[1].ne_cookie, &dotdotcookie, sizeof(dotdotcookie));
266a53f50b9Schristos
267a53f50b9Schristos /*
268a53f50b9Schristos * If map is browsable, call a function make_entry_chain() to construct
269a53f50b9Schristos * a linked list of unmounted keys, and return it. Then link the chain
270a53f50b9Schristos * to the regular list. Get the chain only once, but return
271a53f50b9Schristos * chunks of it each time.
272a53f50b9Schristos */
273a53f50b9Schristos te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
274a53f50b9Schristos if (!te)
275a53f50b9Schristos return 0;
276a53f50b9Schristos if (amuDebug(D_READDIR)) {
277a53f50b9Schristos nfsentry *ne;
278a53f50b9Schristos for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
279a53f50b9Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
280a53f50b9Schristos }
281a53f50b9Schristos
282a53f50b9Schristos /* return only "chain_length" entries */
283a53f50b9Schristos te_next = te;
284a53f50b9Schristos for (i=1; i<chain_length; ++i) {
285a53f50b9Schristos te_next = te_next->ne_nextentry;
286a53f50b9Schristos if (!te_next)
287a53f50b9Schristos break;
288a53f50b9Schristos }
289a53f50b9Schristos if (te_next) {
290a53f50b9Schristos nfsentry *te_saved = te_next->ne_nextentry;
291a53f50b9Schristos te_next->ne_nextentry = NULL; /* terminate "te" chain */
292a53f50b9Schristos te_next = te_saved; /* save rest of "te" for next iteration */
293a53f50b9Schristos dp->dl_eof = FALSE; /* tell readdir there's more */
294a53f50b9Schristos } else {
295a53f50b9Schristos dp->dl_eof = TRUE; /* tell readdir that's it */
296a53f50b9Schristos }
297a53f50b9Schristos ep[1].ne_nextentry = te; /* append this chunk of "te" chain */
298a53f50b9Schristos if (amuDebug(D_READDIR)) {
299a53f50b9Schristos nfsentry *ne;
300a53f50b9Schristos for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
301a53f50b9Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name);
3023d2d5b48Schristos for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) {
3033d2d5b48Schristos u_int cookie;
30423de60b7Schristos (void)memcpy(&cookie, ne->ne_cookie, sizeof(cookie));
30523de60b7Schristos plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d",
3063d2d5b48Schristos j++, ne->ne_name, ne->ne_fileid, cookie);
3073d2d5b48Schristos }
308a53f50b9Schristos plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof);
309a53f50b9Schristos }
310a53f50b9Schristos return 0;
311a53f50b9Schristos } /* end of "if (gen == 0)" statement */
312a53f50b9Schristos
31323de60b7Schristos dlog("%s: real child", __func__);
314a53f50b9Schristos
315a53f50b9Schristos if (gen == DOT_DOT_COOKIE) {
31623de60b7Schristos dlog("%s: End of readdir in %s", __func__, mp->am_path);
317a53f50b9Schristos dp->dl_eof = TRUE;
318a53f50b9Schristos dp->dl_entries = NULL;
319a53f50b9Schristos return 0;
320a53f50b9Schristos }
321a53f50b9Schristos
322a53f50b9Schristos /*
323a53f50b9Schristos * If browsable directories, then continue serving readdir() with another
324a53f50b9Schristos * chunk of entries, starting from where we left off (when gen was equal
325a53f50b9Schristos * to 0). Once again, assume last chunk served to readdir.
326a53f50b9Schristos */
327a53f50b9Schristos dp->dl_eof = TRUE;
328a53f50b9Schristos dp->dl_entries = ep;
329a53f50b9Schristos
330a53f50b9Schristos te = te_next; /* reset 'te' from last saved te_next */
331a53f50b9Schristos if (!te) { /* another indicator of end of readdir */
332a53f50b9Schristos dp->dl_entries = NULL;
333a53f50b9Schristos return 0;
334a53f50b9Schristos }
335a53f50b9Schristos /*
336a53f50b9Schristos * compute # of entries to send in this chain.
337a53f50b9Schristos * heuristics: 128 bytes per entry.
338a53f50b9Schristos */
339a53f50b9Schristos chain_length = count / 128;
340a53f50b9Schristos
341a53f50b9Schristos /* return only "chain_length" entries */
342a53f50b9Schristos for (i = 1; i < chain_length; ++i) {
343a53f50b9Schristos te_next = te_next->ne_nextentry;
344a53f50b9Schristos if (!te_next)
345a53f50b9Schristos break;
346a53f50b9Schristos }
347a53f50b9Schristos if (te_next) {
348a53f50b9Schristos nfsentry *te_saved = te_next->ne_nextentry;
349a53f50b9Schristos te_next->ne_nextentry = NULL; /* terminate "te" chain */
350a53f50b9Schristos te_next = te_saved; /* save rest of "te" for next iteration */
351a53f50b9Schristos dp->dl_eof = FALSE; /* tell readdir there's more */
352a53f50b9Schristos }
353a53f50b9Schristos ep = te; /* send next chunk of "te" chain */
354a53f50b9Schristos dp->dl_entries = ep;
355a53f50b9Schristos if (amuDebug(D_READDIR)) {
356a53f50b9Schristos nfsentry *ne;
357a53f50b9Schristos plog(XLOG_DEBUG, "dl_entries=%p, te_next=%p, dl_eof=%d",
358a53f50b9Schristos dp->dl_entries, te_next, dp->dl_eof);
359a53f50b9Schristos for (ne = te; ne; ne = ne->ne_nextentry)
360a53f50b9Schristos plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name);
361a53f50b9Schristos }
362a53f50b9Schristos return 0;
363a53f50b9Schristos }
364a53f50b9Schristos
36523de60b7Schristos static int
amfs_readdir(am_node * mp,nfscookie cookie,nfsdirlist * dp,nfsentry * ep,u_int count)36623de60b7Schristos amfs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count)
367a53f50b9Schristos {
368a53f50b9Schristos u_int gen = *(u_int *) cookie;
369a53f50b9Schristos am_node *xp;
370a53f50b9Schristos
371a53f50b9Schristos dp->dl_eof = FALSE; /* assume readdir not done */
372a53f50b9Schristos
373a53f50b9Schristos /* when gen is 0, we start reading from the beginning of the directory */
374a53f50b9Schristos if (gen == 0) {
375a53f50b9Schristos /*
376a53f50b9Schristos * In the default instance (which is used to start a search) we return
377a53f50b9Schristos * "." and "..".
378a53f50b9Schristos *
379a53f50b9Schristos * This assumes that the count is big enough to allow both "." and ".."
380a53f50b9Schristos * to be returned in a single packet. If it isn't (which would be
381a53f50b9Schristos * fairly unbelievable) then tough.
382a53f50b9Schristos */
38323de60b7Schristos dlog("%s: default search", __func__);
384a53f50b9Schristos /*
385a53f50b9Schristos * Check for enough room. This is extremely approximate but is more
386a53f50b9Schristos * than enough space. Really need 2 times:
387a53f50b9Schristos * 4byte fileid
388a53f50b9Schristos * 4byte cookie
389a53f50b9Schristos * 4byte name length
390a53f50b9Schristos * 4byte name
391a53f50b9Schristos * plus the dirlist structure */
39223de60b7Schristos #define NEEDROOM (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))
39323de60b7Schristos if (count < NEEDROOM) {
39423de60b7Schristos dlog("%s: not enough room %u < %zu", __func__, count, NEEDROOM);
395a53f50b9Schristos return EINVAL;
39623de60b7Schristos }
397a53f50b9Schristos
398a53f50b9Schristos xp = next_nonerror_node(mp->am_child);
399a53f50b9Schristos dp->dl_entries = ep;
400a53f50b9Schristos
401a53f50b9Schristos /* construct "." */
402a53f50b9Schristos ep[0].ne_fileid = mp->am_gen;
403a53f50b9Schristos ep[0].ne_name = ".";
404a53f50b9Schristos ep[0].ne_nextentry = &ep[1];
40523de60b7Schristos (void)memset(ep[0].ne_cookie, 0, sizeof(u_int));
406a53f50b9Schristos
407a53f50b9Schristos /* construct ".." */
408a53f50b9Schristos if (mp->am_parent)
409a53f50b9Schristos ep[1].ne_fileid = mp->am_parent->am_gen;
410a53f50b9Schristos else
411a53f50b9Schristos ep[1].ne_fileid = mp->am_gen;
412a53f50b9Schristos ep[1].ne_name = "..";
413a53f50b9Schristos ep[1].ne_nextentry = NULL;
41423de60b7Schristos (void)memcpy(ep[1].ne_cookie, (xp ? &xp->am_gen : &dotdotcookie),
41523de60b7Schristos sizeof(dotdotcookie));
416a53f50b9Schristos
417a53f50b9Schristos if (!xp)
418a53f50b9Schristos dp->dl_eof = TRUE; /* by default assume readdir done */
419a53f50b9Schristos
420a53f50b9Schristos if (amuDebug(D_READDIR)) {
421a53f50b9Schristos nfsentry *ne;
422a53f50b9Schristos int j;
4233d2d5b48Schristos for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) {
4243d2d5b48Schristos u_int cookie;
42523de60b7Schristos (void)memcpy(&cookie, ne->ne_cookie, sizeof(cookie));
42623de60b7Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d",
4273d2d5b48Schristos j++, ne->ne_name, ne->ne_fileid, cookie);
4283d2d5b48Schristos }
429a53f50b9Schristos }
430a53f50b9Schristos return 0;
431a53f50b9Schristos }
43223de60b7Schristos dlog("%s: real child", __func__);
433a53f50b9Schristos
434a53f50b9Schristos if (gen == DOT_DOT_COOKIE) {
43523de60b7Schristos dlog("%s: End of readdir in %s", __func__, mp->am_path);
436a53f50b9Schristos dp->dl_eof = TRUE;
437a53f50b9Schristos dp->dl_entries = NULL;
438a53f50b9Schristos if (amuDebug(D_READDIR))
439a53f50b9Schristos plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
440a53f50b9Schristos return 0;
441a53f50b9Schristos }
442a53f50b9Schristos
443a53f50b9Schristos /* non-browsable directories code */
444a53f50b9Schristos xp = mp->am_child;
445a53f50b9Schristos while (xp && xp->am_gen != gen)
446a53f50b9Schristos xp = xp->am_osib;
447a53f50b9Schristos
448a53f50b9Schristos if (xp) {
449a53f50b9Schristos int nbytes = count / 2; /* conservative */
450a53f50b9Schristos int todo = MAX_READDIR_ENTRIES;
451a53f50b9Schristos
452a53f50b9Schristos dp->dl_entries = ep;
453a53f50b9Schristos do {
454a53f50b9Schristos am_node *xp_next = next_nonerror_node(xp->am_osib);
455a53f50b9Schristos
456a53f50b9Schristos if (xp_next) {
45723de60b7Schristos (void)memcpy(ep->ne_cookie, &xp_next->am_gen, sizeof(xp_next->am_gen));
458a53f50b9Schristos } else {
45923de60b7Schristos (void)memcpy(ep->ne_cookie, &dotdotcookie, sizeof(dotdotcookie));
460a53f50b9Schristos dp->dl_eof = TRUE;
461a53f50b9Schristos }
462a53f50b9Schristos
463a53f50b9Schristos ep->ne_fileid = xp->am_gen;
464a53f50b9Schristos ep->ne_name = xp->am_name;
465a53f50b9Schristos nbytes -= sizeof(*ep) + 1;
466a53f50b9Schristos if (xp->am_name)
467a53f50b9Schristos nbytes -= strlen(xp->am_name);
468a53f50b9Schristos
469a53f50b9Schristos xp = xp_next;
470a53f50b9Schristos
471a53f50b9Schristos if (nbytes > 0 && !dp->dl_eof && todo > 1) {
472a53f50b9Schristos ep->ne_nextentry = ep + 1;
473a53f50b9Schristos ep++;
474a53f50b9Schristos --todo;
475a53f50b9Schristos } else {
476a53f50b9Schristos todo = 0;
477a53f50b9Schristos }
478a53f50b9Schristos } while (todo > 0);
479a53f50b9Schristos
480a53f50b9Schristos ep->ne_nextentry = NULL;
481a53f50b9Schristos
482a53f50b9Schristos if (amuDebug(D_READDIR)) {
483a53f50b9Schristos nfsentry *ne;
484a53f50b9Schristos int j;
4853d2d5b48Schristos for (j=0,ne=ep; ne; ne=ne->ne_nextentry) {
4863d2d5b48Schristos u_int cookie;
48723de60b7Schristos (void)memcpy(&cookie, ne->ne_cookie, sizeof(cookie));
48823de60b7Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d",
4893d2d5b48Schristos j++, ne->ne_name, ne->ne_fileid, cookie);
4903d2d5b48Schristos }
491a53f50b9Schristos }
492a53f50b9Schristos return 0;
493a53f50b9Schristos }
494a53f50b9Schristos return ESTALE;
495a53f50b9Schristos }
49623de60b7Schristos
49723de60b7Schristos /*
49823de60b7Schristos * Search a chain for an entry with some name.
49923de60b7Schristos */
50023de60b7Schristos static int
key_already_in_chain3(char * keyname,const am_entry3 * chain)50123de60b7Schristos key_already_in_chain3(char *keyname, const am_entry3 *chain)
50223de60b7Schristos {
50323de60b7Schristos const am_entry3 *tmpchain = chain;
50423de60b7Schristos
50523de60b7Schristos while (tmpchain) {
50623de60b7Schristos if (keyname && tmpchain->name && STREQ(keyname, tmpchain->name))
50723de60b7Schristos return 1;
50823de60b7Schristos tmpchain = tmpchain->nextentry;
50923de60b7Schristos }
51023de60b7Schristos
51123de60b7Schristos return 0;
51223de60b7Schristos }
51323de60b7Schristos
51423de60b7Schristos /*
51523de60b7Schristos * Create a chain of entries which are not linked.
51623de60b7Schristos */
51723de60b7Schristos static am_entry3 *
make_entry_chain3(am_node * mp,const am_entry3 * current_chain,int fully_browsable)51823de60b7Schristos make_entry_chain3(am_node *mp, const am_entry3 *current_chain, int fully_browsable)
51923de60b7Schristos {
52023de60b7Schristos static uint64 last_cookie = (uint64) 2; /* monotonically increasing */
52123de60b7Schristos static am_entry3 chain[MAX_CHAIN];
52223de60b7Schristos static int max_entries = MAX_CHAIN;
52323de60b7Schristos char *key;
52423de60b7Schristos int num_entries = 0, i;
52523de60b7Schristos u_int preflen = 0;
52623de60b7Schristos am_entry3 *retval = (am_entry3 *) NULL;
52723de60b7Schristos mntfs *mf;
52823de60b7Schristos mnt_map *mmp;
52923de60b7Schristos
53023de60b7Schristos if (!mp) {
53123de60b7Schristos plog(XLOG_DEBUG, "make_entry_chain3: mp is (NULL)");
53223de60b7Schristos return retval;
53323de60b7Schristos }
53423de60b7Schristos mf = mp->am_al->al_mnt;
53523de60b7Schristos if (!mf) {
53623de60b7Schristos plog(XLOG_DEBUG, "make_entry_chain3: mp->am_al->al_mnt is (NULL)");
53723de60b7Schristos return retval;
53823de60b7Schristos }
53923de60b7Schristos mmp = (mnt_map *) mf->mf_private;
54023de60b7Schristos if (!mmp) {
54123de60b7Schristos plog(XLOG_DEBUG, "make_entry_chain3: mp->am_al->al_mnt->mf_private is (NULL)");
54223de60b7Schristos return retval;
54323de60b7Schristos }
54423de60b7Schristos
54523de60b7Schristos if (mp->am_pref)
54623de60b7Schristos preflen = strlen(mp->am_pref);
54723de60b7Schristos
54823de60b7Schristos /* iterate over keys */
54923de60b7Schristos for (i = 0; i < NKVHASH; i++) {
55023de60b7Schristos kv *k;
55123de60b7Schristos for (k = mmp->kvhash[i]; k ; k = k->next) {
55223de60b7Schristos
55323de60b7Schristos /*
55423de60b7Schristos * Skip unwanted entries which are either not real entries or
55523de60b7Schristos * very difficult to interpret (wildcards...) This test needs
55623de60b7Schristos * lots of improvement. Any takers?
55723de60b7Schristos */
55823de60b7Schristos key = k->key;
55923de60b7Schristos if (!key)
56023de60b7Schristos continue;
56123de60b7Schristos
56223de60b7Schristos /* Skip '/defaults' */
56323de60b7Schristos if (STREQ(key, "/defaults"))
56423de60b7Schristos continue;
56523de60b7Schristos
56623de60b7Schristos /* Skip '*' */
56723de60b7Schristos if (!fully_browsable && strchr(key, '*'))
56823de60b7Schristos continue;
56923de60b7Schristos
57023de60b7Schristos /*
57123de60b7Schristos * If the map has a prefix-string then check if the key starts with
57223de60b7Schristos * this string, and if it does, skip over this prefix. If it has a
57323de60b7Schristos * prefix and it doesn't match the start of the key, skip it.
57423de60b7Schristos */
57523de60b7Schristos if (preflen) {
57623de60b7Schristos if (preflen > strlen(key))
57723de60b7Schristos continue;
57823de60b7Schristos if (!NSTREQ(key, mp->am_pref, preflen))
57923de60b7Schristos continue;
58023de60b7Schristos key += preflen;
58123de60b7Schristos }
58223de60b7Schristos
58323de60b7Schristos /* no more '/' are allowed, unless browsable_dirs=full was used */
58423de60b7Schristos if (!fully_browsable && strchr(key, '/'))
58523de60b7Schristos continue;
58623de60b7Schristos
58723de60b7Schristos /* no duplicates allowed */
58823de60b7Schristos if (key_already_in_chain3(key, current_chain))
58923de60b7Schristos continue;
59023de60b7Schristos
59123de60b7Schristos /* fill in a cell and link the entry */
59223de60b7Schristos if (num_entries >= max_entries) {
59323de60b7Schristos /* out of space */
59423de60b7Schristos plog(XLOG_DEBUG, "make_entry_chain3: no more space in chain");
59523de60b7Schristos if (num_entries > 0) {
59623de60b7Schristos chain[num_entries - 1].nextentry = NULL;
59723de60b7Schristos retval = &chain[0];
59823de60b7Schristos }
59923de60b7Schristos return retval;
60023de60b7Schristos }
60123de60b7Schristos
60223de60b7Schristos /* we have space. put entry in next cell */
60323de60b7Schristos ++last_cookie;
60423de60b7Schristos chain[num_entries].fileid = last_cookie;
60523de60b7Schristos chain[num_entries].cookie = last_cookie;
60623de60b7Schristos chain[num_entries].name = key;
60723de60b7Schristos if (num_entries < max_entries - 1) { /* link to next one */
60823de60b7Schristos chain[num_entries].nextentry = &chain[num_entries + 1];
60923de60b7Schristos }
61023de60b7Schristos ++num_entries;
61123de60b7Schristos } /* end of "while (k)" */
61223de60b7Schristos } /* end of "for (i ... NKVHASH ..." */
61323de60b7Schristos
61423de60b7Schristos /* terminate chain */
61523de60b7Schristos if (num_entries > 0) {
61623de60b7Schristos chain[num_entries - 1].nextentry = NULL;
61723de60b7Schristos retval = &chain[0];
61823de60b7Schristos }
61923de60b7Schristos
62023de60b7Schristos return retval;
62123de60b7Schristos }
62223de60b7Schristos
needroom3(void)62323de60b7Schristos static size_t needroom3(void)
62423de60b7Schristos {
62523de60b7Schristos /*
62623de60b7Schristos * Check for enough room. This is extremely approximate but should
62723de60b7Schristos * be enough space. Really need 2 times:
62823de60b7Schristos * (8byte fileid
62923de60b7Schristos * 8byte cookie
63023de60b7Schristos * 8byte name pointer
63123de60b7Schristos * 8byte next entry addres) = sizeof(am_entry3)
63223de60b7Schristos * 2byte name + 1byte terminator
63323de60b7Schristos * plus the size of the am_dirlist3 structure */
63423de60b7Schristos return ((2 * ((sizeof(am_entry3) + sizeof("..") + 1))) + sizeof(am_dirlist3));
63523de60b7Schristos }
63623de60b7Schristos
63723de60b7Schristos /* This one is called only if map is browsable */
63823de60b7Schristos static int
amfs_readdir3_browsable(am_node * mp,voidp cookie,am_dirlist3 * dp,am_entry3 * ep,u_int count,int fully_browsable)639*a774e231Schristos amfs_readdir3_browsable(am_node *mp, voidp cookie,
64023de60b7Schristos am_dirlist3 *dp, am_entry3 *ep, u_int count,
64123de60b7Schristos int fully_browsable)
64223de60b7Schristos {
64323de60b7Schristos uint64 gen = *(uint64 *) cookie;
64423de60b7Schristos int chain_length, i;
64523de60b7Schristos static am_entry3 *te, *te_next;
64623de60b7Schristos static int j;
64723de60b7Schristos
64823de60b7Schristos dp->eof = FALSE; /* assume readdir not done */
64923de60b7Schristos
65023de60b7Schristos if (amuDebug(D_READDIR))
651*a774e231Schristos plog(XLOG_DEBUG, "%s: gen=%llu, count=%d", __func__,
652*a774e231Schristos (unsigned long long)gen, count);
65323de60b7Schristos
65423de60b7Schristos if (gen == 0) {
65523de60b7Schristos size_t needed = needroom3();
65623de60b7Schristos /*
65723de60b7Schristos * In the default instance (which is used to start a search) we return
65823de60b7Schristos * "." and "..".
65923de60b7Schristos *
66023de60b7Schristos * This assumes that the count is big enough to allow both "." and ".."
66123de60b7Schristos * to be returned in a single packet. If it isn't (which would be
66223de60b7Schristos * fairly unbelievable) then tough.
66323de60b7Schristos */
66423de60b7Schristos dlog("%s: default search", __func__);
66523de60b7Schristos
66623de60b7Schristos if (count < needed) {
66723de60b7Schristos dlog("%s: not enough room %u < %zu", __func__, count, needed);
66823de60b7Schristos return EINVAL;
66923de60b7Schristos }
67023de60b7Schristos
67123de60b7Schristos /*
67223de60b7Schristos * compute # of entries to send in this chain.
67323de60b7Schristos * heuristics: 128 bytes per entry.
67423de60b7Schristos * This is too much probably, but it seems to work better because
67523de60b7Schristos * of the re-entrant nature of nfs_readdir, and esp. on systems
67623de60b7Schristos * like OpenBSD 2.2.
67723de60b7Schristos */
67823de60b7Schristos chain_length = count / 128;
67923de60b7Schristos
68023de60b7Schristos /* reset static state counters */
68123de60b7Schristos te = te_next = NULL;
68223de60b7Schristos
68323de60b7Schristos dp->entries = ep;
68423de60b7Schristos
68523de60b7Schristos /* construct "." */
68623de60b7Schristos ep[0].fileid = mp->am_gen;
68723de60b7Schristos ep[0].name = ".";
68823de60b7Schristos ep[0].nextentry = &ep[1];
68923de60b7Schristos ep[0].cookie = 0;
69023de60b7Schristos
69123de60b7Schristos /* construct ".." */
69223de60b7Schristos if (mp->am_parent)
69323de60b7Schristos ep[1].fileid = mp->am_parent->am_gen;
69423de60b7Schristos else
69523de60b7Schristos ep[1].fileid = mp->am_gen;
69623de60b7Schristos
69723de60b7Schristos ep[1].name = "..";
69823de60b7Schristos ep[1].nextentry = NULL;
69923de60b7Schristos ep[1].cookie = dotdotcookie;
70023de60b7Schristos
70123de60b7Schristos /*
70223de60b7Schristos * If map is browsable, call a function make_entry_chain() to construct
70323de60b7Schristos * a linked list of unmounted keys, and return it. Then link the chain
70423de60b7Schristos * to the regular list. Get the chain only once, but return
70523de60b7Schristos * chunks of it each time.
70623de60b7Schristos */
70723de60b7Schristos te = make_entry_chain3(mp, dp->entries, fully_browsable);
70823de60b7Schristos if (!te)
70923de60b7Schristos return 0;
71023de60b7Schristos if (amuDebug(D_READDIR)) {
71123de60b7Schristos am_entry3 *ne;
71223de60b7Schristos for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
71323de60b7Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
71423de60b7Schristos }
71523de60b7Schristos
71623de60b7Schristos /* return only "chain_length" entries */
71723de60b7Schristos te_next = te;
71823de60b7Schristos for (i=1; i<chain_length; ++i) {
71923de60b7Schristos te_next = te_next->nextentry;
72023de60b7Schristos if (!te_next)
72123de60b7Schristos break;
72223de60b7Schristos }
72323de60b7Schristos if (te_next) {
72423de60b7Schristos am_entry3 *te_saved = te_next->nextentry;
72523de60b7Schristos te_next->nextentry = NULL; /* terminate "te" chain */
72623de60b7Schristos te_next = te_saved; /* save rest of "te" for next iteration */
72723de60b7Schristos dp->eof = FALSE; /* tell readdir there's more */
72823de60b7Schristos } else {
72923de60b7Schristos dp->eof = TRUE; /* tell readdir that's it */
73023de60b7Schristos }
73123de60b7Schristos ep[1].nextentry = te; /* append this chunk of "te" chain */
73223de60b7Schristos if (amuDebug(D_READDIR)) {
73323de60b7Schristos am_entry3 *ne;
73423de60b7Schristos for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
73523de60b7Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->name);
73623de60b7Schristos for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) {
737*a774e231Schristos plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%llu ck=%llu",
738*a774e231Schristos j++, ne->name, (unsigned long long)ne->fileid,
739*a774e231Schristos (unsigned long long)ne->cookie);
74023de60b7Schristos }
74123de60b7Schristos plog(XLOG_DEBUG, "EOF is %d", dp->eof);
74223de60b7Schristos }
74323de60b7Schristos return 0;
74423de60b7Schristos } /* end of "if (gen == 0)" statement */
74523de60b7Schristos
74623de60b7Schristos dlog("%s: real child", __func__);
74723de60b7Schristos
74823de60b7Schristos if (gen == DOT_DOT_COOKIE) {
74923de60b7Schristos dlog("%s: End of readdir in %s", __func__, mp->am_path);
75023de60b7Schristos dp->eof = TRUE;
75123de60b7Schristos dp->entries = NULL;
75223de60b7Schristos return 0;
75323de60b7Schristos }
75423de60b7Schristos
75523de60b7Schristos /*
75623de60b7Schristos * If browsable directories, then continue serving readdir() with another
75723de60b7Schristos * chunk of entries, starting from where we left off (when gen was equal
75823de60b7Schristos * to 0). Once again, assume last chunk served to readdir.
75923de60b7Schristos */
76023de60b7Schristos dp->eof = TRUE;
76123de60b7Schristos dp->entries = ep;
76223de60b7Schristos
76323de60b7Schristos te = te_next; /* reset 'te' from last saved te_next */
76423de60b7Schristos if (!te) { /* another indicator of end of readdir */
76523de60b7Schristos dp->entries = NULL;
76623de60b7Schristos return 0;
76723de60b7Schristos }
76823de60b7Schristos /*
76923de60b7Schristos * compute # of entries to send in this chain.
77023de60b7Schristos * heuristics: 128 bytes per entry.
77123de60b7Schristos */
77223de60b7Schristos chain_length = count / 128;
77323de60b7Schristos
77423de60b7Schristos /* return only "chain_length" entries */
77523de60b7Schristos for (i = 1; i < chain_length; ++i) {
77623de60b7Schristos te_next = te_next->nextentry;
77723de60b7Schristos if (!te_next)
77823de60b7Schristos break;
77923de60b7Schristos }
78023de60b7Schristos if (te_next) {
78123de60b7Schristos am_entry3 *te_saved = te_next->nextentry;
78223de60b7Schristos te_next->nextentry = NULL; /* terminate "te" chain */
78323de60b7Schristos te_next = te_saved; /* save rest of "te" for next iteration */
78423de60b7Schristos dp->eof = FALSE; /* tell readdir there's more */
78523de60b7Schristos }
78623de60b7Schristos ep = te; /* send next chunk of "te" chain */
78723de60b7Schristos dp->entries = ep;
78823de60b7Schristos if (amuDebug(D_READDIR)) {
78923de60b7Schristos am_entry3 *ne;
79023de60b7Schristos plog(XLOG_DEBUG,
79123de60b7Schristos "entries=%p, te_next=%p, eof=%d", dp->entries, te_next, dp->eof);
79223de60b7Schristos for (ne = te; ne; ne = ne->nextentry)
79323de60b7Schristos plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->name);
79423de60b7Schristos }
79523de60b7Schristos return 0;
79623de60b7Schristos }
79723de60b7Schristos
79823de60b7Schristos static int
amfs_readdir3(am_node * mp,voidp cookie,am_dirlist3 * dp,am_entry3 * ep,u_int count)799*a774e231Schristos amfs_readdir3(am_node *mp, voidp cookie,
80023de60b7Schristos am_dirlist3 *dp, am_entry3 *ep, u_int count)
80123de60b7Schristos {
80223de60b7Schristos uint64 gen = *(uint64 *) cookie;
80323de60b7Schristos am_node *xp;
80423de60b7Schristos
80523de60b7Schristos if (amuDebug(D_READDIR))
806*a774e231Schristos plog(XLOG_DEBUG, "%s: gen=%llu, count=%d", __func__,
807*a774e231Schristos (unsigned long long)gen, count);
80823de60b7Schristos
80923de60b7Schristos dp->eof = FALSE; /* assume readdir not done */
81023de60b7Schristos
81123de60b7Schristos /* when gen is 0, we start reading from the beginning of the directory */
81223de60b7Schristos if (gen == 0) {
81323de60b7Schristos size_t needed = needroom3();
81423de60b7Schristos /*
81523de60b7Schristos * In the default instance (which is used to start a search) we return
81623de60b7Schristos * "." and "..".
81723de60b7Schristos *
81823de60b7Schristos * This assumes that the count is big enough to allow both "." and ".."
81923de60b7Schristos * to be returned in a single packet. If it isn't (which would be
82023de60b7Schristos * fairly unbelievable) then tough.
82123de60b7Schristos */
82223de60b7Schristos dlog("%s: default search", __func__);
82323de60b7Schristos
82423de60b7Schristos if (count < needed) {
82523de60b7Schristos dlog("%s: not enough room %u < %zu", __func__, count, needed);
82623de60b7Schristos return EINVAL;
82723de60b7Schristos }
82823de60b7Schristos
82923de60b7Schristos xp = next_nonerror_node(mp->am_child);
83023de60b7Schristos dp->entries = ep;
83123de60b7Schristos
83223de60b7Schristos /* construct "." */
83323de60b7Schristos ep[0].fileid = mp->am_gen;
83423de60b7Schristos ep[0].name = ".";
83523de60b7Schristos ep[0].cookie = 0;
83623de60b7Schristos ep[0].nextentry = &ep[1];
83723de60b7Schristos
83823de60b7Schristos /* construct ".." */
83923de60b7Schristos if (mp->am_parent)
84023de60b7Schristos ep[1].fileid = mp->am_parent->am_gen;
84123de60b7Schristos else
84223de60b7Schristos ep[1].fileid = mp->am_gen;
84323de60b7Schristos ep[1].name = "..";
84423de60b7Schristos ep[1].nextentry = NULL;
84523de60b7Schristos ep[1].cookie = (xp ? xp->am_gen : dotdotcookie);
84623de60b7Schristos
84723de60b7Schristos if (!xp)
84823de60b7Schristos dp->eof = TRUE; /* by default assume readdir done */
84923de60b7Schristos
85023de60b7Schristos if (amuDebug(D_READDIR)) {
85123de60b7Schristos am_entry3 *ne;
85223de60b7Schristos int j;
85323de60b7Schristos for (j = 0, ne = ep; ne; ne = ne->nextentry) {
854*a774e231Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%llu ck=%llu",
855*a774e231Schristos j++, ne->name, (unsigned long long)ne->fileid,
856*a774e231Schristos (unsigned long long)ne->cookie);
85723de60b7Schristos }
85823de60b7Schristos }
85923de60b7Schristos return 0;
86023de60b7Schristos }
86123de60b7Schristos dlog("%s: real child", __func__);
86223de60b7Schristos
86323de60b7Schristos if (gen == (uint64) DOT_DOT_COOKIE) {
86423de60b7Schristos dlog("%s: End of readdir in %s", __func__, mp->am_path);
86523de60b7Schristos dp->eof = TRUE;
86623de60b7Schristos dp->entries = NULL;
86723de60b7Schristos if (amuDebug(D_READDIR))
86823de60b7Schristos plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
86923de60b7Schristos return 0;
87023de60b7Schristos }
87123de60b7Schristos
87223de60b7Schristos /* non-browsable directories code */
87323de60b7Schristos xp = mp->am_child;
87423de60b7Schristos while (xp && xp->am_gen != gen)
87523de60b7Schristos xp = xp->am_osib;
87623de60b7Schristos
87723de60b7Schristos if (xp) {
87823de60b7Schristos int nbytes = count / 2; /* conservative */
87923de60b7Schristos int todo = MAX_READDIR_ENTRIES;
88023de60b7Schristos
88123de60b7Schristos dp->entries = ep;
88223de60b7Schristos do {
88323de60b7Schristos am_node *xp_next = next_nonerror_node(xp->am_osib);
88423de60b7Schristos
88523de60b7Schristos if (xp_next) {
88623de60b7Schristos ep->cookie = xp_next->am_gen;
88723de60b7Schristos } else {
88823de60b7Schristos ep->cookie = (uint64) dotdotcookie;
88923de60b7Schristos dp->eof = TRUE;
89023de60b7Schristos }
89123de60b7Schristos
89223de60b7Schristos ep->fileid = xp->am_gen;
89323de60b7Schristos ep->name = xp->am_name;
89423de60b7Schristos nbytes -= sizeof(*ep) + 1;
89523de60b7Schristos if (xp->am_name)
89623de60b7Schristos nbytes -= strlen(xp->am_name);
89723de60b7Schristos
89823de60b7Schristos xp = xp_next;
89923de60b7Schristos
90023de60b7Schristos if (nbytes > 0 && !dp->dl_eof && todo > 1) {
90123de60b7Schristos ep->nextentry = ep + 1;
90223de60b7Schristos ep++;
90323de60b7Schristos --todo;
90423de60b7Schristos } else {
90523de60b7Schristos todo = 0;
90623de60b7Schristos }
90723de60b7Schristos } while (todo > 0);
90823de60b7Schristos
90923de60b7Schristos ep->nextentry = NULL;
91023de60b7Schristos
91123de60b7Schristos if (amuDebug(D_READDIR)) {
91223de60b7Schristos am_entry3 *ne;
91323de60b7Schristos int j;
91423de60b7Schristos for (j = 0, ne = ep; ne; ne = ne->nextentry) {
915*a774e231Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%llu ck=%llu",
916*a774e231Schristos j++, ne->name, (unsigned long long)ne->fileid,
917*a774e231Schristos (unsigned long long)ne->cookie);
91823de60b7Schristos }
91923de60b7Schristos }
92023de60b7Schristos return 0;
92123de60b7Schristos }
92223de60b7Schristos return ESTALE;
92323de60b7Schristos }
92423de60b7Schristos
92523de60b7Schristos /*
92623de60b7Schristos * This readdir function which call a special version of it that allows
92723de60b7Schristos * browsing if browsable_dirs=yes was set on the map.
92823de60b7Schristos */
92923de60b7Schristos int
amfs_generic_readdir(am_node * mp,voidp cookie,voidp dp,voidp ep,u_int count)93023de60b7Schristos amfs_generic_readdir(am_node *mp, voidp cookie, voidp dp, voidp ep, u_int count)
93123de60b7Schristos {
93223de60b7Schristos int browsable, full;
93323de60b7Schristos
93423de60b7Schristos /* check if map is browsable */
93523de60b7Schristos browsable = 0;
93623de60b7Schristos if (mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) {
93723de60b7Schristos mntent_t mnt;
93823de60b7Schristos mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts;
93923de60b7Schristos if (amu_hasmntopt(&mnt, "fullybrowsable"))
94023de60b7Schristos browsable = 2;
94123de60b7Schristos else if (amu_hasmntopt(&mnt, "browsable"))
94223de60b7Schristos browsable = 1;
94323de60b7Schristos }
94423de60b7Schristos full = (browsable == 2);
94523de60b7Schristos
94623de60b7Schristos if (nfs_dispatcher == nfs_program_2) {
94723de60b7Schristos if (browsable)
94823de60b7Schristos return amfs_readdir_browsable(mp, cookie, dp, ep, count, full);
94923de60b7Schristos else
95023de60b7Schristos return amfs_readdir(mp, cookie, dp, ep, count);
95123de60b7Schristos } else {
95223de60b7Schristos if (browsable)
953*a774e231Schristos return amfs_readdir3_browsable(mp, cookie, dp, ep, count, full);
95423de60b7Schristos else
955*a774e231Schristos return amfs_readdir3(mp, cookie, dp, ep, count);
95623de60b7Schristos }
95723de60b7Schristos }
958