1*8bc49d51Schristos /* $NetBSD: readdir.c,v 1.4 2015/01/18 16:37:05 christos Exp $ */
2a53f50b9Schristos
3a53f50b9Schristos /*
44da6d876Schristos * 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.
214da6d876Schristos * 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
634da6d876Schristos 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 }
1094da6d876Schristos mf = mp->am_al->al_mnt;
110a53f50b9Schristos if (!mf) {
1114da6d876Schristos 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) {
1164da6d876Schristos 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;
1794da6d876Schristos chain[num_entries].ne_fileid = last_cookie;
1804da6d876Schristos (void)memcpy(chain[num_entries].ne_cookie, &last_cookie,
1814da6d876Schristos 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 */
2254da6d876Schristos 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];
2554da6d876Schristos (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;
2654da6d876Schristos (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);
302c8909e95Schristos for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) {
303c8909e95Schristos u_int cookie;
3044da6d876Schristos (void)memcpy(&cookie, ne->ne_cookie, sizeof(cookie));
3054da6d876Schristos plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d",
306c8909e95Schristos j++, ne->ne_name, ne->ne_fileid, cookie);
307c8909e95Schristos }
308a53f50b9Schristos plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof);
309a53f50b9Schristos }
310a53f50b9Schristos return 0;
311a53f50b9Schristos } /* end of "if (gen == 0)" statement */
312a53f50b9Schristos
3134da6d876Schristos dlog("%s: real child", __func__);
314a53f50b9Schristos
315a53f50b9Schristos if (gen == DOT_DOT_COOKIE) {
3164da6d876Schristos 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
3654da6d876Schristos static int
amfs_readdir(am_node * mp,nfscookie cookie,nfsdirlist * dp,nfsentry * ep,u_int count)3664da6d876Schristos 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 */
3834da6d876Schristos 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 */
3924da6d876Schristos #define NEEDROOM (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))
3934da6d876Schristos if (count < NEEDROOM) {
3944da6d876Schristos dlog("%s: not enough room %u < %zu", __func__, count, NEEDROOM);
395a53f50b9Schristos return EINVAL;
3964da6d876Schristos }
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];
4054da6d876Schristos (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;
4144da6d876Schristos (void)memcpy(ep[1].ne_cookie, (xp ? &xp->am_gen : &dotdotcookie),
4154da6d876Schristos 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;
423c8909e95Schristos for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) {
424c8909e95Schristos u_int cookie;
4254da6d876Schristos (void)memcpy(&cookie, ne->ne_cookie, sizeof(cookie));
4264da6d876Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d",
427c8909e95Schristos j++, ne->ne_name, ne->ne_fileid, cookie);
428c8909e95Schristos }
429a53f50b9Schristos }
430a53f50b9Schristos return 0;
431a53f50b9Schristos }
4324da6d876Schristos dlog("%s: real child", __func__);
433a53f50b9Schristos
434a53f50b9Schristos if (gen == DOT_DOT_COOKIE) {
4354da6d876Schristos 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) {
4574da6d876Schristos (void)memcpy(ep->ne_cookie, &xp_next->am_gen, sizeof(xp_next->am_gen));
458a53f50b9Schristos } else {
4594da6d876Schristos (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;
485c8909e95Schristos for (j=0,ne=ep; ne; ne=ne->ne_nextentry) {
486c8909e95Schristos u_int cookie;
4874da6d876Schristos (void)memcpy(&cookie, ne->ne_cookie, sizeof(cookie));
4884da6d876Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d",
489c8909e95Schristos j++, ne->ne_name, ne->ne_fileid, cookie);
490c8909e95Schristos }
491a53f50b9Schristos }
492a53f50b9Schristos return 0;
493a53f50b9Schristos }
494a53f50b9Schristos return ESTALE;
495a53f50b9Schristos }
4964da6d876Schristos
4974da6d876Schristos /*
4984da6d876Schristos * Search a chain for an entry with some name.
4994da6d876Schristos */
5004da6d876Schristos static int
key_already_in_chain3(char * keyname,const am_entry3 * chain)5014da6d876Schristos key_already_in_chain3(char *keyname, const am_entry3 *chain)
5024da6d876Schristos {
5034da6d876Schristos const am_entry3 *tmpchain = chain;
5044da6d876Schristos
5054da6d876Schristos while (tmpchain) {
5064da6d876Schristos if (keyname && tmpchain->name && STREQ(keyname, tmpchain->name))
5074da6d876Schristos return 1;
5084da6d876Schristos tmpchain = tmpchain->nextentry;
5094da6d876Schristos }
5104da6d876Schristos
5114da6d876Schristos return 0;
5124da6d876Schristos }
5134da6d876Schristos
5144da6d876Schristos /*
5154da6d876Schristos * Create a chain of entries which are not linked.
5164da6d876Schristos */
5174da6d876Schristos static am_entry3 *
make_entry_chain3(am_node * mp,const am_entry3 * current_chain,int fully_browsable)5184da6d876Schristos make_entry_chain3(am_node *mp, const am_entry3 *current_chain, int fully_browsable)
5194da6d876Schristos {
5204da6d876Schristos static uint64 last_cookie = (uint64) 2; /* monotonically increasing */
5214da6d876Schristos static am_entry3 chain[MAX_CHAIN];
5224da6d876Schristos static int max_entries = MAX_CHAIN;
5234da6d876Schristos char *key;
5244da6d876Schristos int num_entries = 0, i;
5254da6d876Schristos u_int preflen = 0;
5264da6d876Schristos am_entry3 *retval = (am_entry3 *) NULL;
5274da6d876Schristos mntfs *mf;
5284da6d876Schristos mnt_map *mmp;
5294da6d876Schristos
5304da6d876Schristos if (!mp) {
5314da6d876Schristos plog(XLOG_DEBUG, "make_entry_chain3: mp is (NULL)");
5324da6d876Schristos return retval;
5334da6d876Schristos }
5344da6d876Schristos mf = mp->am_al->al_mnt;
5354da6d876Schristos if (!mf) {
5364da6d876Schristos plog(XLOG_DEBUG, "make_entry_chain3: mp->am_al->al_mnt is (NULL)");
5374da6d876Schristos return retval;
5384da6d876Schristos }
5394da6d876Schristos mmp = (mnt_map *) mf->mf_private;
5404da6d876Schristos if (!mmp) {
5414da6d876Schristos plog(XLOG_DEBUG, "make_entry_chain3: mp->am_al->al_mnt->mf_private is (NULL)");
5424da6d876Schristos return retval;
5434da6d876Schristos }
5444da6d876Schristos
5454da6d876Schristos if (mp->am_pref)
5464da6d876Schristos preflen = strlen(mp->am_pref);
5474da6d876Schristos
5484da6d876Schristos /* iterate over keys */
5494da6d876Schristos for (i = 0; i < NKVHASH; i++) {
5504da6d876Schristos kv *k;
5514da6d876Schristos for (k = mmp->kvhash[i]; k ; k = k->next) {
5524da6d876Schristos
5534da6d876Schristos /*
5544da6d876Schristos * Skip unwanted entries which are either not real entries or
5554da6d876Schristos * very difficult to interpret (wildcards...) This test needs
5564da6d876Schristos * lots of improvement. Any takers?
5574da6d876Schristos */
5584da6d876Schristos key = k->key;
5594da6d876Schristos if (!key)
5604da6d876Schristos continue;
5614da6d876Schristos
5624da6d876Schristos /* Skip '/defaults' */
5634da6d876Schristos if (STREQ(key, "/defaults"))
5644da6d876Schristos continue;
5654da6d876Schristos
5664da6d876Schristos /* Skip '*' */
5674da6d876Schristos if (!fully_browsable && strchr(key, '*'))
5684da6d876Schristos continue;
5694da6d876Schristos
5704da6d876Schristos /*
5714da6d876Schristos * If the map has a prefix-string then check if the key starts with
5724da6d876Schristos * this string, and if it does, skip over this prefix. If it has a
5734da6d876Schristos * prefix and it doesn't match the start of the key, skip it.
5744da6d876Schristos */
5754da6d876Schristos if (preflen) {
5764da6d876Schristos if (preflen > strlen(key))
5774da6d876Schristos continue;
5784da6d876Schristos if (!NSTREQ(key, mp->am_pref, preflen))
5794da6d876Schristos continue;
5804da6d876Schristos key += preflen;
5814da6d876Schristos }
5824da6d876Schristos
5834da6d876Schristos /* no more '/' are allowed, unless browsable_dirs=full was used */
5844da6d876Schristos if (!fully_browsable && strchr(key, '/'))
5854da6d876Schristos continue;
5864da6d876Schristos
5874da6d876Schristos /* no duplicates allowed */
5884da6d876Schristos if (key_already_in_chain3(key, current_chain))
5894da6d876Schristos continue;
5904da6d876Schristos
5914da6d876Schristos /* fill in a cell and link the entry */
5924da6d876Schristos if (num_entries >= max_entries) {
5934da6d876Schristos /* out of space */
5944da6d876Schristos plog(XLOG_DEBUG, "make_entry_chain3: no more space in chain");
5954da6d876Schristos if (num_entries > 0) {
5964da6d876Schristos chain[num_entries - 1].nextentry = NULL;
5974da6d876Schristos retval = &chain[0];
5984da6d876Schristos }
5994da6d876Schristos return retval;
6004da6d876Schristos }
6014da6d876Schristos
6024da6d876Schristos /* we have space. put entry in next cell */
6034da6d876Schristos ++last_cookie;
6044da6d876Schristos chain[num_entries].fileid = last_cookie;
6054da6d876Schristos chain[num_entries].cookie = last_cookie;
6064da6d876Schristos chain[num_entries].name = key;
6074da6d876Schristos if (num_entries < max_entries - 1) { /* link to next one */
6084da6d876Schristos chain[num_entries].nextentry = &chain[num_entries + 1];
6094da6d876Schristos }
6104da6d876Schristos ++num_entries;
6114da6d876Schristos } /* end of "while (k)" */
6124da6d876Schristos } /* end of "for (i ... NKVHASH ..." */
6134da6d876Schristos
6144da6d876Schristos /* terminate chain */
6154da6d876Schristos if (num_entries > 0) {
6164da6d876Schristos chain[num_entries - 1].nextentry = NULL;
6174da6d876Schristos retval = &chain[0];
6184da6d876Schristos }
6194da6d876Schristos
6204da6d876Schristos return retval;
6214da6d876Schristos }
6224da6d876Schristos
needroom3(void)6234da6d876Schristos static size_t needroom3(void)
6244da6d876Schristos {
6254da6d876Schristos /*
6264da6d876Schristos * Check for enough room. This is extremely approximate but should
6274da6d876Schristos * be enough space. Really need 2 times:
6284da6d876Schristos * (8byte fileid
6294da6d876Schristos * 8byte cookie
6304da6d876Schristos * 8byte name pointer
6314da6d876Schristos * 8byte next entry addres) = sizeof(am_entry3)
6324da6d876Schristos * 2byte name + 1byte terminator
6334da6d876Schristos * plus the size of the am_dirlist3 structure */
6344da6d876Schristos return ((2 * ((sizeof(am_entry3) + sizeof("..") + 1))) + sizeof(am_dirlist3));
6354da6d876Schristos }
6364da6d876Schristos
6374da6d876Schristos /* This one is called only if map is browsable */
6384da6d876Schristos static int
amfs_readdir3_browsable(am_node * mp,voidp cookie,am_dirlist3 * dp,am_entry3 * ep,u_int count,int fully_browsable)639*8bc49d51Schristos amfs_readdir3_browsable(am_node *mp, voidp cookie,
6404da6d876Schristos am_dirlist3 *dp, am_entry3 *ep, u_int count,
6414da6d876Schristos int fully_browsable)
6424da6d876Schristos {
6434da6d876Schristos uint64 gen = *(uint64 *) cookie;
6444da6d876Schristos int chain_length, i;
6454da6d876Schristos static am_entry3 *te, *te_next;
6464da6d876Schristos static int j;
6474da6d876Schristos
6484da6d876Schristos dp->eof = FALSE; /* assume readdir not done */
6494da6d876Schristos
6504da6d876Schristos if (amuDebug(D_READDIR))
651*8bc49d51Schristos plog(XLOG_DEBUG, "%s: gen=%llu, count=%d", __func__,
652*8bc49d51Schristos (unsigned long long)gen, count);
6534da6d876Schristos
6544da6d876Schristos if (gen == 0) {
6554da6d876Schristos size_t needed = needroom3();
6564da6d876Schristos /*
6574da6d876Schristos * In the default instance (which is used to start a search) we return
6584da6d876Schristos * "." and "..".
6594da6d876Schristos *
6604da6d876Schristos * This assumes that the count is big enough to allow both "." and ".."
6614da6d876Schristos * to be returned in a single packet. If it isn't (which would be
6624da6d876Schristos * fairly unbelievable) then tough.
6634da6d876Schristos */
6644da6d876Schristos dlog("%s: default search", __func__);
6654da6d876Schristos
6664da6d876Schristos if (count < needed) {
6674da6d876Schristos dlog("%s: not enough room %u < %zu", __func__, count, needed);
6684da6d876Schristos return EINVAL;
6694da6d876Schristos }
6704da6d876Schristos
6714da6d876Schristos /*
6724da6d876Schristos * compute # of entries to send in this chain.
6734da6d876Schristos * heuristics: 128 bytes per entry.
6744da6d876Schristos * This is too much probably, but it seems to work better because
6754da6d876Schristos * of the re-entrant nature of nfs_readdir, and esp. on systems
6764da6d876Schristos * like OpenBSD 2.2.
6774da6d876Schristos */
6784da6d876Schristos chain_length = count / 128;
6794da6d876Schristos
6804da6d876Schristos /* reset static state counters */
6814da6d876Schristos te = te_next = NULL;
6824da6d876Schristos
6834da6d876Schristos dp->entries = ep;
6844da6d876Schristos
6854da6d876Schristos /* construct "." */
6864da6d876Schristos ep[0].fileid = mp->am_gen;
6874da6d876Schristos ep[0].name = ".";
6884da6d876Schristos ep[0].nextentry = &ep[1];
6894da6d876Schristos ep[0].cookie = 0;
6904da6d876Schristos
6914da6d876Schristos /* construct ".." */
6924da6d876Schristos if (mp->am_parent)
6934da6d876Schristos ep[1].fileid = mp->am_parent->am_gen;
6944da6d876Schristos else
6954da6d876Schristos ep[1].fileid = mp->am_gen;
6964da6d876Schristos
6974da6d876Schristos ep[1].name = "..";
6984da6d876Schristos ep[1].nextentry = NULL;
6994da6d876Schristos ep[1].cookie = dotdotcookie;
7004da6d876Schristos
7014da6d876Schristos /*
7024da6d876Schristos * If map is browsable, call a function make_entry_chain() to construct
7034da6d876Schristos * a linked list of unmounted keys, and return it. Then link the chain
7044da6d876Schristos * to the regular list. Get the chain only once, but return
7054da6d876Schristos * chunks of it each time.
7064da6d876Schristos */
7074da6d876Schristos te = make_entry_chain3(mp, dp->entries, fully_browsable);
7084da6d876Schristos if (!te)
7094da6d876Schristos return 0;
7104da6d876Schristos if (amuDebug(D_READDIR)) {
7114da6d876Schristos am_entry3 *ne;
7124da6d876Schristos for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
7134da6d876Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
7144da6d876Schristos }
7154da6d876Schristos
7164da6d876Schristos /* return only "chain_length" entries */
7174da6d876Schristos te_next = te;
7184da6d876Schristos for (i=1; i<chain_length; ++i) {
7194da6d876Schristos te_next = te_next->nextentry;
7204da6d876Schristos if (!te_next)
7214da6d876Schristos break;
7224da6d876Schristos }
7234da6d876Schristos if (te_next) {
7244da6d876Schristos am_entry3 *te_saved = te_next->nextentry;
7254da6d876Schristos te_next->nextentry = NULL; /* terminate "te" chain */
7264da6d876Schristos te_next = te_saved; /* save rest of "te" for next iteration */
7274da6d876Schristos dp->eof = FALSE; /* tell readdir there's more */
7284da6d876Schristos } else {
7294da6d876Schristos dp->eof = TRUE; /* tell readdir that's it */
7304da6d876Schristos }
7314da6d876Schristos ep[1].nextentry = te; /* append this chunk of "te" chain */
7324da6d876Schristos if (amuDebug(D_READDIR)) {
7334da6d876Schristos am_entry3 *ne;
7344da6d876Schristos for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
7354da6d876Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->name);
7364da6d876Schristos for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) {
737*8bc49d51Schristos plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%llu ck=%llu",
738*8bc49d51Schristos j++, ne->name, (unsigned long long)ne->fileid,
739*8bc49d51Schristos (unsigned long long)ne->cookie);
7404da6d876Schristos }
7414da6d876Schristos plog(XLOG_DEBUG, "EOF is %d", dp->eof);
7424da6d876Schristos }
7434da6d876Schristos return 0;
7444da6d876Schristos } /* end of "if (gen == 0)" statement */
7454da6d876Schristos
7464da6d876Schristos dlog("%s: real child", __func__);
7474da6d876Schristos
7484da6d876Schristos if (gen == DOT_DOT_COOKIE) {
7494da6d876Schristos dlog("%s: End of readdir in %s", __func__, mp->am_path);
7504da6d876Schristos dp->eof = TRUE;
7514da6d876Schristos dp->entries = NULL;
7524da6d876Schristos return 0;
7534da6d876Schristos }
7544da6d876Schristos
7554da6d876Schristos /*
7564da6d876Schristos * If browsable directories, then continue serving readdir() with another
7574da6d876Schristos * chunk of entries, starting from where we left off (when gen was equal
7584da6d876Schristos * to 0). Once again, assume last chunk served to readdir.
7594da6d876Schristos */
7604da6d876Schristos dp->eof = TRUE;
7614da6d876Schristos dp->entries = ep;
7624da6d876Schristos
7634da6d876Schristos te = te_next; /* reset 'te' from last saved te_next */
7644da6d876Schristos if (!te) { /* another indicator of end of readdir */
7654da6d876Schristos dp->entries = NULL;
7664da6d876Schristos return 0;
7674da6d876Schristos }
7684da6d876Schristos /*
7694da6d876Schristos * compute # of entries to send in this chain.
7704da6d876Schristos * heuristics: 128 bytes per entry.
7714da6d876Schristos */
7724da6d876Schristos chain_length = count / 128;
7734da6d876Schristos
7744da6d876Schristos /* return only "chain_length" entries */
7754da6d876Schristos for (i = 1; i < chain_length; ++i) {
7764da6d876Schristos te_next = te_next->nextentry;
7774da6d876Schristos if (!te_next)
7784da6d876Schristos break;
7794da6d876Schristos }
7804da6d876Schristos if (te_next) {
7814da6d876Schristos am_entry3 *te_saved = te_next->nextentry;
7824da6d876Schristos te_next->nextentry = NULL; /* terminate "te" chain */
7834da6d876Schristos te_next = te_saved; /* save rest of "te" for next iteration */
7844da6d876Schristos dp->eof = FALSE; /* tell readdir there's more */
7854da6d876Schristos }
7864da6d876Schristos ep = te; /* send next chunk of "te" chain */
7874da6d876Schristos dp->entries = ep;
7884da6d876Schristos if (amuDebug(D_READDIR)) {
7894da6d876Schristos am_entry3 *ne;
7904da6d876Schristos plog(XLOG_DEBUG,
7914da6d876Schristos "entries=%p, te_next=%p, eof=%d", dp->entries, te_next, dp->eof);
7924da6d876Schristos for (ne = te; ne; ne = ne->nextentry)
7934da6d876Schristos plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->name);
7944da6d876Schristos }
7954da6d876Schristos return 0;
7964da6d876Schristos }
7974da6d876Schristos
7984da6d876Schristos static int
amfs_readdir3(am_node * mp,voidp cookie,am_dirlist3 * dp,am_entry3 * ep,u_int count)799*8bc49d51Schristos amfs_readdir3(am_node *mp, voidp cookie,
8004da6d876Schristos am_dirlist3 *dp, am_entry3 *ep, u_int count)
8014da6d876Schristos {
8024da6d876Schristos uint64 gen = *(uint64 *) cookie;
8034da6d876Schristos am_node *xp;
8044da6d876Schristos
8054da6d876Schristos if (amuDebug(D_READDIR))
806*8bc49d51Schristos plog(XLOG_DEBUG, "%s: gen=%llu, count=%d", __func__,
807*8bc49d51Schristos (unsigned long long)gen, count);
8084da6d876Schristos
8094da6d876Schristos dp->eof = FALSE; /* assume readdir not done */
8104da6d876Schristos
8114da6d876Schristos /* when gen is 0, we start reading from the beginning of the directory */
8124da6d876Schristos if (gen == 0) {
8134da6d876Schristos size_t needed = needroom3();
8144da6d876Schristos /*
8154da6d876Schristos * In the default instance (which is used to start a search) we return
8164da6d876Schristos * "." and "..".
8174da6d876Schristos *
8184da6d876Schristos * This assumes that the count is big enough to allow both "." and ".."
8194da6d876Schristos * to be returned in a single packet. If it isn't (which would be
8204da6d876Schristos * fairly unbelievable) then tough.
8214da6d876Schristos */
8224da6d876Schristos dlog("%s: default search", __func__);
8234da6d876Schristos
8244da6d876Schristos if (count < needed) {
8254da6d876Schristos dlog("%s: not enough room %u < %zu", __func__, count, needed);
8264da6d876Schristos return EINVAL;
8274da6d876Schristos }
8284da6d876Schristos
8294da6d876Schristos xp = next_nonerror_node(mp->am_child);
8304da6d876Schristos dp->entries = ep;
8314da6d876Schristos
8324da6d876Schristos /* construct "." */
8334da6d876Schristos ep[0].fileid = mp->am_gen;
8344da6d876Schristos ep[0].name = ".";
8354da6d876Schristos ep[0].cookie = 0;
8364da6d876Schristos ep[0].nextentry = &ep[1];
8374da6d876Schristos
8384da6d876Schristos /* construct ".." */
8394da6d876Schristos if (mp->am_parent)
8404da6d876Schristos ep[1].fileid = mp->am_parent->am_gen;
8414da6d876Schristos else
8424da6d876Schristos ep[1].fileid = mp->am_gen;
8434da6d876Schristos ep[1].name = "..";
8444da6d876Schristos ep[1].nextentry = NULL;
8454da6d876Schristos ep[1].cookie = (xp ? xp->am_gen : dotdotcookie);
8464da6d876Schristos
8474da6d876Schristos if (!xp)
8484da6d876Schristos dp->eof = TRUE; /* by default assume readdir done */
8494da6d876Schristos
8504da6d876Schristos if (amuDebug(D_READDIR)) {
8514da6d876Schristos am_entry3 *ne;
8524da6d876Schristos int j;
8534da6d876Schristos for (j = 0, ne = ep; ne; ne = ne->nextentry) {
854*8bc49d51Schristos plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%llu ck=%llu",
855*8bc49d51Schristos j++, ne->name, (unsigned long long)ne->fileid,
856*8bc49d51Schristos (unsigned long long)ne->cookie);
8574da6d876Schristos }
8584da6d876Schristos }
8594da6d876Schristos return 0;
8604da6d876Schristos }
8614da6d876Schristos dlog("%s: real child", __func__);
8624da6d876Schristos
8634da6d876Schristos if (gen == (uint64) DOT_DOT_COOKIE) {
8644da6d876Schristos dlog("%s: End of readdir in %s", __func__, mp->am_path);
8654da6d876Schristos dp->eof = TRUE;
8664da6d876Schristos dp->entries = NULL;
8674da6d876Schristos if (amuDebug(D_READDIR))
8684da6d876Schristos plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
8694da6d876Schristos return 0;
8704da6d876Schristos }
8714da6d876Schristos
8724da6d876Schristos /* non-browsable directories code */
8734da6d876Schristos xp = mp->am_child;
8744da6d876Schristos while (xp && xp->am_gen != gen)
8754da6d876Schristos xp = xp->am_osib;
8764da6d876Schristos
8774da6d876Schristos if (xp) {
8784da6d876Schristos int nbytes = count / 2; /* conservative */
8794da6d876Schristos int todo = MAX_READDIR_ENTRIES;
8804da6d876Schristos
8814da6d876Schristos dp->entries = ep;
8824da6d876Schristos do {
8834da6d876Schristos am_node *xp_next = next_nonerror_node(xp->am_osib);
8844da6d876Schristos
8854da6d876Schristos if (xp_next) {
8864da6d876Schristos ep->cookie = xp_next->am_gen;
8874da6d876Schristos } else {
8884da6d876Schristos ep->cookie = (uint64) dotdotcookie;
8894da6d876Schristos dp->eof = TRUE;
8904da6d876Schristos }
8914da6d876Schristos
8924da6d876Schristos ep->fileid = xp->am_gen;
8934da6d876Schristos ep->name = xp->am_name;
8944da6d876Schristos nbytes -= sizeof(*ep) + 1;
8954da6d876Schristos if (xp->am_name)
8964da6d876Schristos nbytes -= strlen(xp->am_name);
8974da6d876Schristos
8984da6d876Schristos xp = xp_next;
8994da6d876Schristos
9004da6d876Schristos if (nbytes > 0 && !dp->dl_eof && todo > 1) {
9014da6d876Schristos ep->nextentry = ep + 1;
9024da6d876Schristos ep++;
9034da6d876Schristos --todo;
9044da6d876Schristos } else {
9054da6d876Schristos todo = 0;
9064da6d876Schristos }
9074da6d876Schristos } while (todo > 0);
9084da6d876Schristos
9094da6d876Schristos ep->nextentry = NULL;
9104da6d876Schristos
9114da6d876Schristos if (amuDebug(D_READDIR)) {
9124da6d876Schristos am_entry3 *ne;
9134da6d876Schristos int j;
9144da6d876Schristos for (j = 0, ne = ep; ne; ne = ne->nextentry) {
915*8bc49d51Schristos plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%llu ck=%llu",
916*8bc49d51Schristos j++, ne->name, (unsigned long long)ne->fileid,
917*8bc49d51Schristos (unsigned long long)ne->cookie);
9184da6d876Schristos }
9194da6d876Schristos }
9204da6d876Schristos return 0;
9214da6d876Schristos }
9224da6d876Schristos return ESTALE;
9234da6d876Schristos }
9244da6d876Schristos
9254da6d876Schristos /*
9264da6d876Schristos * This readdir function which call a special version of it that allows
9274da6d876Schristos * browsing if browsable_dirs=yes was set on the map.
9284da6d876Schristos */
9294da6d876Schristos int
amfs_generic_readdir(am_node * mp,voidp cookie,voidp dp,voidp ep,u_int count)9304da6d876Schristos amfs_generic_readdir(am_node *mp, voidp cookie, voidp dp, voidp ep, u_int count)
9314da6d876Schristos {
9324da6d876Schristos int browsable, full;
9334da6d876Schristos
9344da6d876Schristos /* check if map is browsable */
9354da6d876Schristos browsable = 0;
9364da6d876Schristos if (mp->am_al->al_mnt && mp->am_al->al_mnt->mf_mopts) {
9374da6d876Schristos mntent_t mnt;
9384da6d876Schristos mnt.mnt_opts = mp->am_al->al_mnt->mf_mopts;
9394da6d876Schristos if (amu_hasmntopt(&mnt, "fullybrowsable"))
9404da6d876Schristos browsable = 2;
9414da6d876Schristos else if (amu_hasmntopt(&mnt, "browsable"))
9424da6d876Schristos browsable = 1;
9434da6d876Schristos }
9444da6d876Schristos full = (browsable == 2);
9454da6d876Schristos
9464da6d876Schristos if (nfs_dispatcher == nfs_program_2) {
9474da6d876Schristos if (browsable)
9484da6d876Schristos return amfs_readdir_browsable(mp, cookie, dp, ep, count, full);
9494da6d876Schristos else
9504da6d876Schristos return amfs_readdir(mp, cookie, dp, ep, count);
9514da6d876Schristos } else {
9524da6d876Schristos if (browsable)
953*8bc49d51Schristos return amfs_readdir3_browsable(mp, cookie, dp, ep, count, full);
9544da6d876Schristos else
955*8bc49d51Schristos return amfs_readdir3(mp, cookie, dp, ep, count);
9564da6d876Schristos }
9574da6d876Schristos }
958