static char sccsid[] = "@(#)telldir.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libc/gen/telldir.c,v 1.11 2008/05/05 14:05:23 kib Exp $");
+__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <sys/param.h>
#include "libc_private.h"
#include "telldir.h"
-/*
- * The option SINGLEUSE may be defined to say that a telldir
- * cookie may be used only once before it is freed. This option
- * is used to avoid having memory usage grow without bound.
- */
-#if !__DARWIN_UNIX03
-#define SINGLEUSE
-#endif /* !__DARWIN_UNIX03 */
-
/*
* return a pointer into a directory
*/
long
-telldir(dirp)
- DIR *dirp;
+telldir(DIR *dirp)
{
struct ddloc *lp;
+ long idx;
-#if __DARWIN_UNIX03
if (__isthreaded)
_pthread_mutex_lock(&dirp->dd_lock);
LIST_FOREACH(lp, &dirp->dd_td->td_locq, loc_lqe) {
- if (
#if __DARWIN_64_BIT_INO_T
- (lp->loc_seek == dirp->dd_td->seekoff)
+ if (lp->loc_seek == dirp->dd_td->seekoff &&
#else /* !__DARWIN_64_BIT_INO_T */
- (lp->loc_seek == dirp->dd_seek)
+ if (lp->loc_seek == dirp->dd_seek &&
#endif /* __DARWIN_64_BIT_INO_T */
- && (lp->loc_loc == dirp->dd_loc))
- goto found;
- }
- if ((lp = (struct ddloc *)malloc(sizeof(struct ddloc))) == NULL) {
- if (__isthreaded)
- _pthread_mutex_unlock(&dirp->dd_lock);
- return (-1);
+ lp->loc_loc == dirp->dd_loc)
+ break;
}
-#else /* !__DARWIN_UNIX03 */
- if ((lp = (struct ddloc *)malloc(sizeof(struct ddloc))) == NULL)
- return (-1);
- if (__isthreaded)
- _pthread_mutex_lock(&dirp->dd_lock);
-#endif /* __DARWIN_UNIX03 */
- lp->loc_index = dirp->dd_td->td_loccnt++;
+ if (lp == NULL) {
+ lp = malloc(sizeof(struct ddloc));
+ if (lp == NULL) {
+ if (__isthreaded)
+ _pthread_mutex_unlock(&dirp->dd_lock);
+ return (-1);
+ }
+ lp->loc_index = dirp->dd_td->td_loccnt++;
#if __DARWIN_64_BIT_INO_T
- lp->loc_seek = dirp->dd_td->seekoff;
+ lp->loc_seek = dirp->dd_td->seekoff;
#else /* !__DARWIN_64_BIT_INO_T */
- lp->loc_seek = dirp->dd_seek;
+ lp->loc_seek = dirp->dd_seek;
#endif /* __DARWIN_64_BIT_INO_T */
- lp->loc_loc = dirp->dd_loc;
- LIST_INSERT_HEAD(&dirp->dd_td->td_locq, lp, loc_lqe);
-#if __DARWIN_UNIX03
-found:
-#endif /* __DARWIN_UNIX03 */
+ lp->loc_loc = dirp->dd_loc;
+ LIST_INSERT_HEAD(&dirp->dd_td->td_locq, lp, loc_lqe);
+ }
+ idx = lp->loc_index;
if (__isthreaded)
_pthread_mutex_unlock(&dirp->dd_lock);
- return (lp->loc_index);
+ return (idx);
}
/*
* Only values returned by "telldir" should be passed to seekdir.
*/
void
-_seekdir(dirp, loc)
- DIR *dirp;
- long loc;
+_seekdir(DIR *dirp, long loc)
{
struct ddloc *lp;
struct dirent *dp;
}
if (lp == NULL)
return;
- if (lp->loc_loc == dirp->dd_loc &&
#if __DARWIN_64_BIT_INO_T
- lp->loc_seek == dirp->dd_td->seekoff
+ if (lp->loc_loc == dirp->dd_loc && lp->loc_seek == dirp->dd_td->seekoff)
#else /* !__DARWIN_64_BIT_INO_T */
- lp->loc_seek == dirp->dd_seek
+ if (lp->loc_loc == dirp->dd_loc && lp->loc_seek == dirp->dd_seek)
#endif /* __DARWIN_64_BIT_INO_T */
- )
- goto found;
+ return;
+
+ /* If it's within the same chunk of data, don't bother reloading. */
+#if __DARWIN_64_BIT_INO_T
+ if (lp->loc_seek == dirp->dd_td->seekoff) {
+#else /* !__DARWIN_64_BIT_INO_T */
+ if (lp->loc_seek == dirp->dd_seek) {
+#endif /* __DARWIN_64_BIT_INO_T */
+ /*
+ * If we go back to 0 don't make the next readdir
+ * trigger a call to getdirentries().
+ */
+ if (lp->loc_loc == 0)
+ dirp->dd_flags |= __DTF_SKIPREAD;
+ dirp->dd_loc = lp->loc_loc;
+ return;
+ }
(void) lseek(dirp->dd_fd, (off_t)lp->loc_seek, SEEK_SET);
#if __DARWIN_64_BIT_INO_T
dirp->dd_td->seekoff = lp->loc_seek;
dirp->dd_seek = lp->loc_seek;
#endif /* __DARWIN_64_BIT_INO_T */
dirp->dd_loc = 0;
+ dirp->dd_flags &= ~(__DTF_SKIPREAD | __DTF_ATEND); /* current contents are invalid */
while (dirp->dd_loc < lp->loc_loc) {
dp = _readdir_unlocked(dirp, 0);
if (dp == NULL)
break;
}
-found:;
-#ifdef SINGLEUSE
- LIST_REMOVE(lp, loc_lqe);
- free((caddr_t)lp);
-#endif
+}
+
+/*
+ * After readdir returns the last entry in a block, a call to telldir
+ * returns a location that is after the end of that last entry.
+ * However, that location doesn't refer to a valid directory entry.
+ * Ideally, the call to telldir would return a location that refers to
+ * the first entry in the next block. That location is not known
+ * until the next block is read, so readdir calls this function after
+ * fetching a new block to fix any such telldir locations.
+ */
+void
+_fixtelldir(DIR *dirp, long oldseek, long oldloc)
+{
+ struct ddloc *lp;
+
+ lp = LIST_FIRST(&dirp->dd_td->td_locq);
+ if (lp != NULL) {
+ if (lp->loc_loc == oldloc &&
+ lp->loc_seek == oldseek) {
+#if __DARWIN_64_BIT_INO_T
+ lp->loc_seek = dirp->dd_td->seekoff;
+#else /* !__DARWIN_64_BIT_INO_T */
+ lp->loc_seek = dirp->dd_seek;
+#endif /* __DARWIN_64_BIT_INO_T */
+ lp->loc_loc = dirp->dd_loc;
+ }
+ }
}
#ifndef BUILDING_VARIANT
* Reclaim memory for telldir cookies which weren't used.
*/
void
-_reclaim_telldir(dirp)
- DIR *dirp;
+_reclaim_telldir(DIR *dirp)
{
struct ddloc *lp;
struct ddloc *templp;
}
LIST_INIT(&dirp->dd_td->td_locq);
}
-#endif /* !BUILDING_VARIANT */
+#endif