xref: /netbsd-src/external/bsd/am-utils/dist/amd/readdir.c (revision 8bc49d51dd1abfd6ff03d24c2237ae447507de63)
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