about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/telldir.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2020-04-13 18:09:20 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2023-12-29 16:48:51 -0300
commit283cd0eca5b50a77372f2f201acd24cd223db310 (patch)
tree8dee2fbab353697c67f2fbdb6d4b7b2137414478 /sysdeps/unix/sysv/linux/telldir.c
parent5c2728e451fa15b97b5161ffd20cf647b54f39e6 (diff)
downloadglibc-283cd0eca5b50a77372f2f201acd24cd223db310.tar.gz
glibc-283cd0eca5b50a77372f2f201acd24cd223db310.tar.xz
glibc-283cd0eca5b50a77372f2f201acd24cd223db310.zip
linux: Set internal DIR filepos as off64_t (BZ #23960, BZ #24050) azanella/bz23960-dirent
It allows to obtain the expected entry offset on telldir and set
it correctly on seekdir on platforms where long int is smaller
than off64_t.

On such cases opendir creates a map entry between the DIR d_off
offset and the returned long int (the telldir return value).
seekdir will then set the correct offset from the internal list
using the telldir as the list key.

It also removes the overflow check on readdir and the returned value
will be truncated by the non-LFS off_t size.  As Joseph has noted
in BZ #23960 comment #22, d_off is an opaque value and since
telldir/seekdir works regardless of the returned dirent d_off value.

Finally it removes the requirement to check for overflow values on
telldir (BZ #24050).

Checked on x86_64-linux-gnu, i686-linux-gnu, powerpc-linux-gnu,
and arm-linux-gnueabihf.
Diffstat (limited to 'sysdeps/unix/sysv/linux/telldir.c')
-rw-r--r--sysdeps/unix/sysv/linux/telldir.c36
1 files changed, 36 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/telldir.c b/sysdeps/unix/sysv/linux/telldir.c
index 1e5c129e9f..1ac4fce50c 100644
--- a/sysdeps/unix/sysv/linux/telldir.c
+++ b/sysdeps/unix/sysv/linux/telldir.c
@@ -15,9 +15,12 @@
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <stdio.h>
+#include <assert.h>
 #include <dirent.h>
 
 #include <dirstream.h>
+#include <telldir.h>
 
 /* Return the current position of DIRP.  */
 long int
@@ -26,7 +29,40 @@ telldir (DIR *dirp)
   long int ret;
 
   __libc_lock_lock (dirp->lock);
+
+#if _DIRENT_OFFSET_TRANSLATION
+  /* If the directory position fits in the packet structure, returns it.
+     Otherwise, check if the position is already been recorded in the
+     dynamic array.  If not, add the new record.  */
+
+  union dirstream_packed dsp;
+
+  if (!telldir_need_dirstream (dirp->filepos))
+    {
+      dsp.p.is_packed = 1;
+      dsp.p.info = dirp->filepos;
+    }
+  else
+    {
+      dsp.l = -1;
+
+      size_t i;
+      for (i = 0; ;i++)
+	{
+	  /* It should be pre-allocated on readdir.  */
+	  assert (i < dirstream_loc_size (&dirp->locs));
+	  if (*dirstream_loc_at (&dirp->locs, i) == dirp->filepos)
+	    break;
+	}
+
+      dsp.p.is_packed = 0;
+      dsp.p.info = i;
+    }
+
+  ret = dsp.l;
+#else
   ret = dirp->filepos;
+#endif
   __libc_lock_unlock (dirp->lock);
 
   return ret;