readdir.c revision 174295
1192873Sweongyo/*
2192873Sweongyo * Copyright (c) 1997-2006 Erez Zadok
3192873Sweongyo * Copyright (c) 1990 Jan-Simon Pendry
4192873Sweongyo * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5192873Sweongyo * Copyright (c) 1990 The Regents of the University of California.
6192873Sweongyo * All rights reserved.
7192873Sweongyo *
8192873Sweongyo * This code is derived from software contributed to Berkeley by
9192873Sweongyo * Jan-Simon Pendry at Imperial College, London.
10192873Sweongyo *
11192873Sweongyo * Redistribution and use in source and binary forms, with or without
12192873Sweongyo * modification, are permitted provided that the following conditions
13192873Sweongyo * are met:
14192873Sweongyo * 1. Redistributions of source code must retain the above copyright
15192873Sweongyo *    notice, this list of conditions and the following disclaimer.
16192873Sweongyo * 2. Redistributions in binary form must reproduce the above copyright
17192873Sweongyo *    notice, this list of conditions and the following disclaimer in the
18192873Sweongyo *    documentation and/or other materials provided with the distribution.
19192873Sweongyo * 3. All advertising materials mentioning features or use of this software
20192873Sweongyo *    must display the following acknowledgment:
21192873Sweongyo *      This product includes software developed by the University of
22192873Sweongyo *      California, Berkeley and its contributors.
23192873Sweongyo * 4. Neither the name of the University nor the names of its contributors
24192873Sweongyo *    may be used to endorse or promote products derived from this software
25192873Sweongyo *    without specific prior written permission.
26192873Sweongyo *
27192873Sweongyo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28192873Sweongyo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29192873Sweongyo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30192873Sweongyo * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31192873Sweongyo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32192873Sweongyo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33192873Sweongyo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34192873Sweongyo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35192873Sweongyo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36192873Sweongyo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37192873Sweongyo * SUCH DAMAGE.
38192873Sweongyo *
39192873Sweongyo *
40192873Sweongyo * File: am-utils/amd/readdir.c
41192873Sweongyo *
42192873Sweongyo */
43192873Sweongyo
44192873Sweongyo
45192873Sweongyo#ifdef HAVE_CONFIG_H
46192873Sweongyo# include <config.h>
47192873Sweongyo#endif /* HAVE_CONFIG_H */
48192873Sweongyo#include <am_defs.h>
49192873Sweongyo#include <amd.h>
50192873Sweongyo
51192873Sweongyo
52192873Sweongyo/****************************************************************************
53192873Sweongyo *** MACROS                                                               ***
54194677Sthompsa ****************************************************************************/
55192873Sweongyo#define DOT_DOT_COOKIE	(u_int) 1
56192873Sweongyo#define MAX_CHAIN	2048
57192873Sweongyo
58192873Sweongyo
59192873Sweongyo/****************************************************************************
60227309Sed *** FORWARD DEFINITIONS                                                  ***
61192873Sweongyo ****************************************************************************/
62192873Sweongyostatic int key_already_in_chain(char *keyname, const nfsentry *chain);
63242126Shselaskystatic nfsentry *make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable);
64192873Sweongyostatic int amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable);
65192873Sweongyo
66192873Sweongyo
67192873Sweongyo/****************************************************************************
68192873Sweongyo *** FUNCTIONS                                                             ***
69192873Sweongyo ****************************************************************************/
70192873Sweongyo/*
71192873Sweongyo * Was: NEW_TOPLVL_READDIR
72192873Sweongyo * Search a chain for an entry with some name.
73192873Sweongyo * -Erez Zadok <ezk@cs.columbia.edu>
74192873Sweongyo */
75198194Sweongyostatic int
76192873Sweongyokey_already_in_chain(char *keyname, const nfsentry *chain)
77192873Sweongyo{
78192873Sweongyo  const nfsentry *tmpchain = chain;
79192873Sweongyo
80192873Sweongyo  while (tmpchain) {
81192873Sweongyo    if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name))
82192873Sweongyo        return 1;
83192873Sweongyo    tmpchain = tmpchain->ne_nextentry;
84192873Sweongyo  }
85192873Sweongyo
86192873Sweongyo  return 0;
87194099Sthompsa}
88242126Shselasky
89192873Sweongyo
90192873Sweongyo/*
91192873Sweongyo * Create a chain of entries which are not linked.
92192873Sweongyo * -Erez Zadok <ezk@cs.columbia.edu>
93192873Sweongyo */
94192873Sweongyostatic nfsentry *
95192873Sweongyomake_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable)
96192873Sweongyo{
97192873Sweongyo  static u_int last_cookie = (u_int) 2;	/* monotonically increasing */
98192873Sweongyo  static nfsentry chain[MAX_CHAIN];
99192873Sweongyo  static int max_entries = MAX_CHAIN;
100192873Sweongyo  char *key;
101223486Shselasky  int num_entries = 0, i;
102197761Sweongyo  u_int preflen = 0;
103197761Sweongyo  nfsentry *retval = (nfsentry *) NULL;
104197761Sweongyo  mntfs *mf;
105197761Sweongyo  mnt_map *mmp;
106197761Sweongyo
107197761Sweongyo  if (!mp) {
108197761Sweongyo    plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)");
109197761Sweongyo    return retval;
110197761Sweongyo  }
111197761Sweongyo  mf = mp->am_mnt;
112197761Sweongyo  if (!mf) {
113197761Sweongyo    plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt is (NULL)");
114223446Sgavin    return retval;
115192873Sweongyo  }
116192873Sweongyo  mmp = (mnt_map *) mf->mf_private;
117192873Sweongyo  if (!mmp) {
118192873Sweongyo    plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt->mf_private is (NULL)");
119192873Sweongyo    return retval;
120192873Sweongyo  }
121192873Sweongyo
122192873Sweongyo  if (mp->am_pref)
123192873Sweongyo    preflen = strlen(mp->am_pref);
124192873Sweongyo
125192873Sweongyo  /* iterate over keys */
126192873Sweongyo  for (i = 0; i < NKVHASH; i++) {
127192873Sweongyo    kv *k;
128192873Sweongyo    for (k = mmp->kvhash[i]; k ; k = k->next) {
129192873Sweongyo
130192873Sweongyo      /*
131192873Sweongyo       * Skip unwanted entries which are either not real entries or
132192873Sweongyo       * very difficult to interpret (wildcards...)  This test needs
133192873Sweongyo       * lots of improvement.  Any takers?
134192873Sweongyo       */
135192873Sweongyo      key = k->key;
136192873Sweongyo      if (!key)
137192873Sweongyo	continue;
138192873Sweongyo
139192873Sweongyo      /* Skip '/defaults' */
140192873Sweongyo      if (STREQ(key, "/defaults"))
141192873Sweongyo	continue;
142192873Sweongyo
143192873Sweongyo      /* Skip '*' */
144192873Sweongyo      if (!fully_browsable && strchr(key, '*'))
145192873Sweongyo	continue;
146192873Sweongyo
147192873Sweongyo      /*
148192873Sweongyo       * If the map has a prefix-string then check if the key starts with
149192873Sweongyo       * this string, and if it does, skip over this prefix.  If it has a
150192873Sweongyo       * prefix and it doesn't match the start of the key, skip it.
151192873Sweongyo       */
152192873Sweongyo      if (preflen) {
153192873Sweongyo	if (preflen > strlen(key))
154192873Sweongyo	  continue;
155192873Sweongyo	if (!NSTREQ(key, mp->am_pref, preflen))
156192873Sweongyo	  continue;
157192873Sweongyo	key += preflen;
158192873Sweongyo      }
159192873Sweongyo
160192873Sweongyo      /* no more '/' are allowed, unless browsable_dirs=full was used */
161192873Sweongyo      if (!fully_browsable && strchr(key, '/'))
162192873Sweongyo	continue;
163192873Sweongyo
164192873Sweongyo      /* no duplicates allowed */
165192873Sweongyo      if (key_already_in_chain(key, current_chain))
166192873Sweongyo	continue;
167192873Sweongyo
168192873Sweongyo      /* fill in a cell and link the entry */
169192873Sweongyo      if (num_entries >= max_entries) {
170192873Sweongyo	/* out of space */
171192873Sweongyo	plog(XLOG_DEBUG, "make_entry_chain: no more space in chain");
172192873Sweongyo	if (num_entries > 0) {
173192873Sweongyo	  chain[num_entries - 1].ne_nextentry = 0;
174192873Sweongyo	  retval = &chain[0];
175192873Sweongyo	}
176192873Sweongyo	return retval;
177192873Sweongyo      }
178192873Sweongyo
179192873Sweongyo      /* we have space.  put entry in next cell */
180192873Sweongyo      ++last_cookie;
181192873Sweongyo      chain[num_entries].ne_fileid = (u_int) last_cookie;
182192873Sweongyo      *(u_int *) chain[num_entries].ne_cookie = (u_int) last_cookie;
183192873Sweongyo      chain[num_entries].ne_name = key;
184192873Sweongyo      if (num_entries < max_entries - 1) {	/* link to next one */
185192873Sweongyo	chain[num_entries].ne_nextentry = &chain[num_entries + 1];
186192873Sweongyo      }
187192873Sweongyo      ++num_entries;
188192873Sweongyo    } /* end of "while (k)" */
189192873Sweongyo  } /* end of "for (i ... NKVHASH ..." */
190192873Sweongyo
191196970Sphk  /* terminate chain */
192196970Sphk  if (num_entries > 0) {
193196970Sphk    chain[num_entries - 1].ne_nextentry = 0;
194196970Sphk    retval = &chain[0];
195196970Sphk  }
196196970Sphk
197196970Sphk  return retval;
198196970Sphk}
199196970Sphk
200196970Sphk
201196970Sphk
202196970Sphk/* This one is called only if map is browsable */
203196970Sphkstatic int
204196970Sphkamfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable)
205196970Sphk{
206192873Sweongyo  u_int gen = *(u_int *) cookie;
207192873Sweongyo  int chain_length, i;
208192873Sweongyo  static nfsentry *te, *te_next;
209192873Sweongyo  static int j;
210192873Sweongyo
211192873Sweongyo  dp->dl_eof = FALSE;		/* assume readdir not done */
212192873Sweongyo
213192873Sweongyo  if (amuDebug(D_READDIR))
214192873Sweongyo    plog(XLOG_DEBUG, "amfs_readdir_browsable gen=%u, count=%d",
215192873Sweongyo	 gen, count);
216192873Sweongyo
217192873Sweongyo  if (gen == 0) {
218192873Sweongyo    /*
219192873Sweongyo     * In the default instance (which is used to start a search) we return
220192873Sweongyo     * "." and "..".
221192873Sweongyo     *
222192873Sweongyo     * This assumes that the count is big enough to allow both "." and ".."
223192873Sweongyo     * to be returned in a single packet.  If it isn't (which would be
224192873Sweongyo     * fairly unbelievable) then tough.
225192873Sweongyo     */
226192873Sweongyo    dlog("amfs_readdir_browsable: default search");
227192873Sweongyo    /*
228192873Sweongyo     * Check for enough room.  This is extremely approximate but is more
229192873Sweongyo     * than enough space.  Really need 2 times:
230192873Sweongyo     *      4byte fileid
231192873Sweongyo     *      4byte cookie
232192873Sweongyo     *      4byte name length
233192873Sweongyo     *      4byte name
234192873Sweongyo     * plus the dirlist structure */
235192873Sweongyo    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
236192873Sweongyo      return EINVAL;
237192873Sweongyo
238196970Sphk    /*
239192873Sweongyo     * compute # of entries to send in this chain.
240192873Sweongyo     * heuristics: 128 bytes per entry.
241192873Sweongyo     * This is too much probably, but it seems to work better because
242192873Sweongyo     * of the re-entrant nature of nfs_readdir, and esp. on systems
243192873Sweongyo     * like OpenBSD 2.2.
244192873Sweongyo     */
245192873Sweongyo    chain_length = count / 128;
246192873Sweongyo
247192873Sweongyo    /* reset static state counters */
248192873Sweongyo    te = te_next = NULL;
249192873Sweongyo
250192873Sweongyo    dp->dl_entries = ep;
251192873Sweongyo
252192873Sweongyo    /* construct "." */
253192873Sweongyo    ep[0].ne_fileid = mp->am_gen;
254192873Sweongyo    ep[0].ne_name = ".";
255192873Sweongyo    ep[0].ne_nextentry = &ep[1];
256192873Sweongyo    *(u_int *) ep[0].ne_cookie = 0;
257192873Sweongyo
258192873Sweongyo    /* construct ".." */
259192873Sweongyo    if (mp->am_parent)
260192873Sweongyo      ep[1].ne_fileid = mp->am_parent->am_gen;
261192873Sweongyo    else
262192873Sweongyo      ep[1].ne_fileid = mp->am_gen;
263192873Sweongyo
264192873Sweongyo    ep[1].ne_name = "..";
265192873Sweongyo    ep[1].ne_nextentry = 0;
266192873Sweongyo    *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE;
267192873Sweongyo
268192873Sweongyo    /*
269192873Sweongyo     * If map is browsable, call a function make_entry_chain() to construct
270192873Sweongyo     * a linked list of unmounted keys, and return it.  Then link the chain
271192873Sweongyo     * to the regular list.  Get the chain only once, but return
272192873Sweongyo     * chunks of it each time.
273192873Sweongyo     */
274192873Sweongyo    te = make_entry_chain(mp, dp->dl_entries, fully_browsable);
275192873Sweongyo    if (!te)
276192873Sweongyo      return 0;
277192873Sweongyo    if (amuDebug(D_READDIR)) {
278192873Sweongyo      nfsentry *ne;
279192873Sweongyo      for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
280196970Sphk	plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name);
281192873Sweongyo    }
282192873Sweongyo
283192873Sweongyo    /* return only "chain_length" entries */
284192873Sweongyo    te_next = te;
285192873Sweongyo    for (i=1; i<chain_length; ++i) {
286192873Sweongyo      te_next = te_next->ne_nextentry;
287192873Sweongyo      if (!te_next)
288192873Sweongyo	break;
289192873Sweongyo    }
290192873Sweongyo    if (te_next) {
291192873Sweongyo      nfsentry *te_saved = te_next->ne_nextentry;
292192873Sweongyo      te_next->ne_nextentry = NULL; /* terminate "te" chain */
293192873Sweongyo      te_next = te_saved;	/* save rest of "te" for next iteration */
294192873Sweongyo      dp->dl_eof = FALSE;	/* tell readdir there's more */
295192873Sweongyo    } else {
296192873Sweongyo      dp->dl_eof = TRUE;	/* tell readdir that's it */
297192873Sweongyo    }
298192873Sweongyo    ep[1].ne_nextentry = te;	/* append this chunk of "te" chain */
299192873Sweongyo    if (amuDebug(D_READDIR)) {
300192873Sweongyo      nfsentry *ne;
301192873Sweongyo      for (j = 0, ne = te; ne; ne = ne->ne_nextentry)
302192873Sweongyo	plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name);
303192873Sweongyo      for (j = 0, ne = ep; ne; ne = ne->ne_nextentry)
304192873Sweongyo	plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d",
305196970Sphk	     j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
306192873Sweongyo      plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof);
307192873Sweongyo    }
308192873Sweongyo    return 0;
309192873Sweongyo  } /* end of "if (gen == 0)" statement */
310192873Sweongyo
311192873Sweongyo  dlog("amfs_readdir_browsable: real child");
312192873Sweongyo
313192873Sweongyo  if (gen == DOT_DOT_COOKIE) {
314192873Sweongyo    dlog("amfs_readdir_browsable: End of readdir in %s", mp->am_path);
315192873Sweongyo    dp->dl_eof = TRUE;
316192873Sweongyo    dp->dl_entries = 0;
317192873Sweongyo    return 0;
318192873Sweongyo  }
319192873Sweongyo
320192873Sweongyo  /*
321192873Sweongyo   * If browsable directories, then continue serving readdir() with another
322192873Sweongyo   * chunk of entries, starting from where we left off (when gen was equal
323192873Sweongyo   * to 0).  Once again, assume last chunk served to readdir.
324192873Sweongyo   */
325192873Sweongyo  dp->dl_eof = TRUE;
326198194Sweongyo  dp->dl_entries = ep;
327192873Sweongyo
328192873Sweongyo  te = te_next;			/* reset 'te' from last saved te_next */
329192873Sweongyo  if (!te) {			/* another indicator of end of readdir */
330192873Sweongyo    dp->dl_entries = 0;
331192873Sweongyo    return 0;
332192873Sweongyo  }
333198194Sweongyo  /*
334198194Sweongyo   * compute # of entries to send in this chain.
335198194Sweongyo   * heuristics: 128 bytes per entry.
336198194Sweongyo   */
337198194Sweongyo  chain_length = count / 128;
338198194Sweongyo
339198194Sweongyo  /* return only "chain_length" entries */
340198194Sweongyo  for (i = 1; i < chain_length; ++i) {
341198194Sweongyo    te_next = te_next->ne_nextentry;
342198194Sweongyo    if (!te_next)
343198194Sweongyo      break;
344198194Sweongyo  }
345198194Sweongyo  if (te_next) {
346198194Sweongyo    nfsentry *te_saved = te_next->ne_nextentry;
347198194Sweongyo    te_next->ne_nextentry = NULL; /* terminate "te" chain */
348198194Sweongyo    te_next = te_saved;		/* save rest of "te" for next iteration */
349198194Sweongyo    dp->dl_eof = FALSE;		/* tell readdir there's more */
350198194Sweongyo  }
351198194Sweongyo  ep = te;			/* send next chunk of "te" chain */
352198194Sweongyo  dp->dl_entries = ep;
353198194Sweongyo  if (amuDebug(D_READDIR)) {
354198194Sweongyo    nfsentry *ne;
355198194Sweongyo    plog(XLOG_DEBUG, "dl_entries=%p, te_next=%p, dl_eof=%d",
356198194Sweongyo	 dp->dl_entries, te_next, dp->dl_eof);
357198194Sweongyo    for (ne = te; ne; ne = ne->ne_nextentry)
358198194Sweongyo      plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name);
359198194Sweongyo  }
360198194Sweongyo  return 0;
361198194Sweongyo}
362198194Sweongyo
363198194Sweongyo
364198194Sweongyo/*
365198194Sweongyo * This readdir function which call a special version of it that allows
366198194Sweongyo * browsing if browsable_dirs=yes was set on the map.
367192873Sweongyo */
368192873Sweongyoint
369192873Sweongyoamfs_generic_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count)
370192873Sweongyo{
371192873Sweongyo  u_int gen = *(u_int *) cookie;
372192873Sweongyo  am_node *xp;
373192873Sweongyo  mntent_t mnt;
374192873Sweongyo
375192873Sweongyo  dp->dl_eof = FALSE;		/* assume readdir not done */
376192873Sweongyo
377192873Sweongyo  /* check if map is browsable */
378192873Sweongyo  if (mp->am_mnt && mp->am_mnt->mf_mopts) {
379192873Sweongyo    mnt.mnt_opts = mp->am_mnt->mf_mopts;
380192873Sweongyo    if (amu_hasmntopt(&mnt, "fullybrowsable"))
381192873Sweongyo      return amfs_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
382192873Sweongyo    if (amu_hasmntopt(&mnt, "browsable"))
383192873Sweongyo      return amfs_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
384192873Sweongyo  }
385192873Sweongyo
386192873Sweongyo  /* when gen is 0, we start reading from the beginning of the directory */
387192873Sweongyo  if (gen == 0) {
388192873Sweongyo    /*
389192873Sweongyo     * In the default instance (which is used to start a search) we return
390192873Sweongyo     * "." and "..".
391192873Sweongyo     *
392192873Sweongyo     * This assumes that the count is big enough to allow both "." and ".."
393192873Sweongyo     * to be returned in a single packet.  If it isn't (which would be
394192873Sweongyo     * fairly unbelievable) then tough.
395192873Sweongyo     */
396192873Sweongyo    dlog("amfs_generic_readdir: default search");
397192873Sweongyo    /*
398192873Sweongyo     * Check for enough room.  This is extremely approximate but is more
399192873Sweongyo     * than enough space.  Really need 2 times:
400192873Sweongyo     *      4byte fileid
401192873Sweongyo     *      4byte cookie
402192873Sweongyo     *      4byte name length
403192873Sweongyo     *      4byte name
404192873Sweongyo     * plus the dirlist structure */
405192873Sweongyo    if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp))))
406192873Sweongyo      return EINVAL;
407192873Sweongyo
408192873Sweongyo    xp = next_nonerror_node(mp->am_child);
409192873Sweongyo    dp->dl_entries = ep;
410192873Sweongyo
411192873Sweongyo    /* construct "." */
412192873Sweongyo    ep[0].ne_fileid = mp->am_gen;
413192873Sweongyo    ep[0].ne_name = ".";
414192873Sweongyo    ep[0].ne_nextentry = &ep[1];
415192873Sweongyo    *(u_int *) ep[0].ne_cookie = 0;
416192873Sweongyo
417192873Sweongyo    /* construct ".." */
418192873Sweongyo    if (mp->am_parent)
419192873Sweongyo      ep[1].ne_fileid = mp->am_parent->am_gen;
420192873Sweongyo    else
421192873Sweongyo      ep[1].ne_fileid = mp->am_gen;
422192873Sweongyo    ep[1].ne_name = "..";
423192873Sweongyo    ep[1].ne_nextentry = 0;
424192873Sweongyo    *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE);
425192873Sweongyo
426192873Sweongyo    if (!xp)
427192873Sweongyo      dp->dl_eof = TRUE;	/* by default assume readdir done */
428192873Sweongyo
429192873Sweongyo    if (amuDebug(D_READDIR)) {
430192873Sweongyo      nfsentry *ne;
431192873Sweongyo      int j;
432192873Sweongyo      for (j = 0, ne = ep; ne; ne = ne->ne_nextentry)
433192873Sweongyo	plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d",
434192873Sweongyo	     j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
435192873Sweongyo    }
436192873Sweongyo    return 0;
437192873Sweongyo  }
438192873Sweongyo  dlog("amfs_generic_readdir: real child");
439192873Sweongyo
440192873Sweongyo  if (gen == DOT_DOT_COOKIE) {
441192873Sweongyo    dlog("amfs_generic_readdir: End of readdir in %s", mp->am_path);
442192873Sweongyo    dp->dl_eof = TRUE;
443192873Sweongyo    dp->dl_entries = 0;
444192873Sweongyo    if (amuDebug(D_READDIR))
445196970Sphk      plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n");
446192873Sweongyo    return 0;
447192873Sweongyo  }
448196970Sphk
449196970Sphk  /* non-browsable directories code */
450196970Sphk  xp = mp->am_child;
451196970Sphk  while (xp && xp->am_gen != gen)
452192873Sweongyo    xp = xp->am_osib;
453192873Sweongyo
454192873Sweongyo  if (xp) {
455192873Sweongyo    int nbytes = count / 2;	/* conservative */
456192873Sweongyo    int todo = MAX_READDIR_ENTRIES;
457192873Sweongyo
458192873Sweongyo    dp->dl_entries = ep;
459192873Sweongyo    do {
460192873Sweongyo      am_node *xp_next = next_nonerror_node(xp->am_osib);
461192873Sweongyo
462192873Sweongyo      if (xp_next) {
463192873Sweongyo	*(u_int *) ep->ne_cookie = xp_next->am_gen;
464192873Sweongyo      } else {
465192873Sweongyo	*(u_int *) ep->ne_cookie = DOT_DOT_COOKIE;
466192873Sweongyo	dp->dl_eof = TRUE;
467192873Sweongyo      }
468192873Sweongyo
469192873Sweongyo      ep->ne_fileid = xp->am_gen;
470192873Sweongyo      ep->ne_name = xp->am_name;
471192873Sweongyo      nbytes -= sizeof(*ep) + 1;
472192873Sweongyo      if (xp->am_name)
473192873Sweongyo	nbytes -= strlen(xp->am_name);
474192873Sweongyo
475192873Sweongyo      xp = xp_next;
476260284Sdim
477196970Sphk      if (nbytes > 0 && !dp->dl_eof && todo > 1) {
478196970Sphk	ep->ne_nextentry = ep + 1;
479196970Sphk	ep++;
480196970Sphk	--todo;
481196970Sphk      } else {
482196970Sphk	todo = 0;
483196970Sphk      }
484196970Sphk    } while (todo > 0);
485196970Sphk
486196970Sphk    ep->ne_nextentry = 0;
487196970Sphk
488196970Sphk    if (amuDebug(D_READDIR)) {
489196970Sphk      nfsentry *ne;
490196970Sphk      int j;
491196970Sphk      for (j=0,ne=ep; ne; ne=ne->ne_nextentry)
492196970Sphk	plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d",
493196970Sphk	     j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie);
494196970Sphk    }
495196970Sphk    return 0;
496196970Sphk  }
497196970Sphk  return ESTALE;
498196970Sphk}
499196970Sphk