Hi,

after some more testing and proofreading, i found some issues
with my libc/gen patch; please delete what i sent yesterday.

Here is an updated patch which now works correctly with Otto's
regression test, with the new test i just committed, and with
the test from the Perl test suite Andrew pointed out, even with
threads enabled.  It also survived quite some manual testing.

Changes with respect to yesterday:

 * Unfortunately, dirent.d_off is the offset of the *next*
   entry with respect to the beginning of the directory,
   not of the *current* entry.  So, we simply don't know
   where the first entry in dd_buf is located in the directory,
   so we cannot seek back to it without calling getdents() again.
   Thus, we can as well continue using dd_loc = 0 as the flag
   to invalidate dd_buf, no use changing it to -1.
 * Due to the same misunderstanding of d_off, i introduced an
   off-by-one error, sometimes backing up one entry too far.
   Ouch.
 * Initialize dd_size in opendir() and test it in seekdir()
   to make sure that we don't access dd_buf uninitialized.

Comments?  Tests?  OKs?
  Ingo

P.S.
Oh, and as requested by Theo, i'm now fixing the bug 90 years
earlier than i originally intended.


Index: opendir.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/opendir.c,v
retrieving revision 1.25
diff -u -p -r1.25 opendir.c
--- opendir.c   6 Oct 2013 17:57:11 -0000       1.25
+++ opendir.c   3 Nov 2013 00:23:09 -0000
@@ -111,6 +111,7 @@ __fdopendir(int fd)
                return (NULL);
        }
 
+       dirp->dd_size = 0;
        dirp->dd_loc = 0;
        dirp->dd_fd = fd;
        dirp->dd_lock = NULL;
Index: rewinddir.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/rewinddir.c,v
retrieving revision 1.10
diff -u -p -r1.10 rewinddir.c
--- rewinddir.c 13 Aug 2013 05:52:12 -0000      1.10
+++ rewinddir.c 3 Nov 2013 00:23:09 -0000
@@ -1,5 +1,5 @@
 /*     $OpenBSD: rewinddir.c,v 1.10 2013/08/13 05:52:12 guenther Exp $ */
-/*-
+/*
  * Copyright (c) 1990, 1993
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -28,16 +28,12 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/types.h>
 #include <dirent.h>
-
 #include "thread_private.h"
 #include "telldir.h"
 
 void
 rewinddir(DIR *dirp)
 {
-       _MUTEX_LOCK(&dirp->dd_lock);
-       __seekdir(dirp, 0);
-       _MUTEX_UNLOCK(&dirp->dd_lock);
+       seekdir(dirp, 0);
 }
Index: seekdir.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/seekdir.c,v
retrieving revision 1.9
diff -u -p -r1.9 seekdir.c
--- seekdir.c   5 Jun 2007 18:11:48 -0000       1.9
+++ seekdir.c   3 Nov 2013 00:23:09 -0000
@@ -1,46 +1,71 @@
 /*     $OpenBSD: seekdir.c,v 1.9 2007/06/05 18:11:48 kurt Exp $ */
 /*
- * Copyright (c) 1983, 1993
- *     The Regents of the University of California.  All rights reserved.
+ * Copyright (c) 2013 Ingo Schwarze <schwa...@openbsd.org>
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <sys/param.h>
 #include <dirent.h>
+#include <unistd.h>
+
 #include "thread_private.h"
 #include "telldir.h"
 
 /*
  * Seek to an entry in a directory.
- * __seekdir is in telldir.c so that it can share opaque data structures.
+ * Only values returned by "telldir" should be passed to seekdir.
  */
 void
 seekdir(DIR *dirp, long loc)
 {
+       struct dirent *dp;
+
+       /*
+        * First check whether the directory entry to seek for
+        * is still buffered in the directory structure in memory.
+        */
+
        _MUTEX_LOCK(&dirp->dd_lock);
-       __seekdir(dirp, loc);
+       dp = (struct dirent *)dirp->dd_buf;
+       if (dirp->dd_size > 0 && dp->d_off <= loc) {
+               for (dirp->dd_loc = 0;
+                    dirp->dd_loc < dirp->dd_size;
+                    dirp->dd_loc += dp->d_reclen) {
+                       dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc);
+                       if (dp->d_off < loc)
+                               continue;
+
+                       /*
+                        * Entry found in the buffer, use it.
+                        * If readdir(3) follows, this will
+                        * save us a getdents(2) syscall.
+                        * Note that d_off is the offset of
+                        * the _next_ entry, so advance dd_loc.
+                        */
+
+                       dirp->dd_curpos = loc;
+                       dirp->dd_loc += dp->d_reclen;
+                       _MUTEX_UNLOCK(&dirp->dd_lock);
+                       return;
+               }
+       }
+
+       /*
+        * The entry is not in the buffer, prepare a call to getdents(2).
+        * In particular, invalidate dd_loc.
+        */
+
+       dirp->dd_loc = 0;
+       dirp->dd_curpos = lseek(dirp->dd_fd, loc, SEEK_SET);
        _MUTEX_UNLOCK(&dirp->dd_lock);
 }
Index: telldir.c
===================================================================
RCS file: /cvs/src/lib/libc/gen/telldir.c,v
retrieving revision 1.15
diff -u -p -r1.15 telldir.c
--- telldir.c   16 Aug 2013 05:27:39 -0000      1.15
+++ telldir.c   3 Nov 2013 00:23:09 -0000
@@ -28,44 +28,21 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/param.h>
-#include <sys/queue.h>
 #include <dirent.h>
-#include <stdlib.h>
-#include <unistd.h>
-
 #include "thread_private.h"
 #include "telldir.h"
 
-int _readdir_unlocked(DIR *, struct dirent **, int);
-
 /*
  * return a pointer into a directory
  */
 long
-_telldir_unlocked(DIR *dirp)
-{
-       return (dirp->dd_curpos);
-}
-
-long
 telldir(DIR *dirp)
 {
        long i;
 
        _MUTEX_LOCK(&dirp->dd_lock);
-       i = _telldir_unlocked(dirp);
+       i = dirp->dd_curpos;
        _MUTEX_UNLOCK(&dirp->dd_lock);
 
        return (i);
-}
-
-/*
- * seek to an entry in a directory.
- * Only values returned by "telldir" should be passed to seekdir.
- */
-void
-__seekdir(DIR *dirp, long loc)
-{
-       dirp->dd_curpos = lseek(dirp->dd_fd, loc, SEEK_SET);
 }
Index: telldir.h
===================================================================
RCS file: /cvs/src/lib/libc/gen/telldir.h,v
retrieving revision 1.6
diff -u -p -r1.6 telldir.h
--- telldir.h   13 Aug 2013 05:52:13 -0000      1.6
+++ telldir.h   3 Nov 2013 00:23:09 -0000
@@ -47,7 +47,4 @@ struct _dirdesc {
        void    *dd_lock;       /* mutex to protect struct */
 };
 
-long           _telldir_unlocked(DIR *);
-void           __seekdir(DIR *, long);
-
 #endif

Reply via email to