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