about summary refs log tree commit diff
path: root/sysdeps/unix/sysv/linux/getdents.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux/getdents.c')
-rw-r--r--sysdeps/unix/sysv/linux/getdents.c191
1 files changed, 81 insertions, 110 deletions
diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c
index 1f2404e142..d438547be9 100644
--- a/sysdeps/unix/sysv/linux/getdents.c
+++ b/sysdeps/unix/sysv/linux/getdents.c
@@ -33,21 +33,6 @@
 
 #include <kernel-features.h>
 
-#ifdef __NR_getdents64
-# ifndef __ASSUME_GETDENTS64_SYSCALL
-#  ifndef __GETDENTS
-/* The variable is shared between all *getdents* calls.  */
-int __have_no_getdents64 attribute_hidden;
-#  else
-extern int __have_no_getdents64 attribute_hidden;
-#  endif
-#  define have_no_getdents64_defined 1
-# endif
-#endif
-#ifndef have_no_getdents64_defined
-# define __have_no_getdents64 0
-#endif
-
 /* For Linux we need a special version of this file since the
    definition of `struct dirent' is not the same for the kernel and
    the libc.  There is one additional field which might be introduced
@@ -137,108 +122,94 @@ __GETDENTS (int fd, char *buf, size_t nbytes)
   off64_t last_offset = -1;
 
 #ifdef __NR_getdents64
-  if (!__have_no_getdents64)
+  {
+    union
     {
-# ifndef __ASSUME_GETDENTS64_SYSCALL
-      int saved_errno = errno;
-# endif
-      union
+      struct kernel_dirent64 k;
+      DIRENT_TYPE u;
+      char b[1];
+    } *kbuf = (void *) buf, *outp, *inp;
+    size_t kbytes = nbytes;
+    if (offsetof (DIRENT_TYPE, d_name)
+	< offsetof (struct kernel_dirent64, d_name)
+	&& nbytes <= sizeof (DIRENT_TYPE))
       {
-	struct kernel_dirent64 k;
-	DIRENT_TYPE u;
-	char b[1];
-      } *kbuf = (void *) buf, *outp, *inp;
-      size_t kbytes = nbytes;
-      if (offsetof (DIRENT_TYPE, d_name)
-	  < offsetof (struct kernel_dirent64, d_name)
-	  && nbytes <= sizeof (DIRENT_TYPE))
-	{
-	  kbytes = nbytes + offsetof (struct kernel_dirent64, d_name)
-		   - offsetof (DIRENT_TYPE, d_name);
-	  kbuf = __alloca(kbytes);
-	}
-      retval = INLINE_SYSCALL (getdents64, 3, fd, kbuf, kbytes);
-# ifndef __ASSUME_GETDENTS64_SYSCALL
-      if (retval != -1 || (errno != EINVAL && errno != ENOSYS))
-# endif
-	{
-	  const size_t size_diff = (offsetof (struct kernel_dirent64, d_name)
-				    - offsetof (DIRENT_TYPE, d_name));
+	kbytes = (nbytes + offsetof (struct kernel_dirent64, d_name)
+		  - offsetof (DIRENT_TYPE, d_name));
+	kbuf = __alloca(kbytes);
+      }
+    retval = INLINE_SYSCALL (getdents64, 3, fd, kbuf, kbytes);
+    const size_t size_diff = (offsetof (struct kernel_dirent64, d_name)
+			      - offsetof (DIRENT_TYPE, d_name));
+
+    /* Return the error if encountered.  */
+    if (retval == -1)
+      return -1;
+
+    /* If the structure returned by the kernel is identical to what we
+       need, don't do any conversions.  */
+    if (offsetof (DIRENT_TYPE, d_name)
+	== offsetof (struct kernel_dirent64, d_name)
+	&& sizeof (outp->u.d_ino) == sizeof (inp->k.d_ino)
+	&& sizeof (outp->u.d_off) == sizeof (inp->k.d_off))
+      return retval;
+
+    /* These two pointers might alias the same memory buffer.
+       Standard C requires that we always use the same type for them,
+       so we must use the union type.  */
+    inp = kbuf;
+    outp = (void *) buf;
+
+    while (&inp->b < &kbuf->b + retval)
+      {
+	const size_t alignment = __alignof__ (DIRENT_TYPE);
+	/* Since inp->k.d_reclen is already aligned for the kernel
+	   structure this may compute a value that is bigger
+	   than necessary.  */
+	size_t old_reclen = inp->k.d_reclen;
+	size_t new_reclen = ((old_reclen - size_diff + alignment - 1)
+			     & ~(alignment - 1));
+
+	/* Copy the data out of the old structure into temporary space.
+	   Then copy the name, which may overlap if BUF == KBUF.  */
+	const uint64_t d_ino = inp->k.d_ino;
+	const int64_t d_off = inp->k.d_off;
+	const uint8_t d_type = inp->k.d_type;
 
-	  /* Return the error if encountered.  */
-	  if (retval == -1)
+	memmove (outp->u.d_name, inp->k.d_name,
+		 old_reclen - offsetof (struct kernel_dirent64, d_name));
+
+	/* Now we have copied the data from INP and access only OUTP.  */
+
+	DIRENT_SET_DP_INO (&outp->u, d_ino);
+	outp->u.d_off = d_off;
+	if ((sizeof (outp->u.d_ino) != sizeof (inp->k.d_ino)
+	     && outp->u.d_ino != d_ino)
+	    || (sizeof (outp->u.d_off) != sizeof (inp->k.d_off)
+		&& outp->u.d_off != d_off))
+	  {
+	    /* Overflow.  If there was at least one entry
+	       before this one, return them without error,
+	       otherwise signal overflow.  */
+	    if (last_offset != -1)
+	      {
+		__lseek64 (fd, last_offset, SEEK_SET);
+		return outp->b - buf;
+	      }
+	    __set_errno (EOVERFLOW);
 	    return -1;
+	  }
 
-	  /* If the structure returned by the kernel is identical to what we
-	     need, don't do any conversions.  */
-	  if (offsetof (DIRENT_TYPE, d_name)
-	      == offsetof (struct kernel_dirent64, d_name)
-	      && sizeof (outp->u.d_ino) == sizeof (inp->k.d_ino)
-	      && sizeof (outp->u.d_off) == sizeof (inp->k.d_off))
-	    return retval;
-
-	  /* These two pointers might alias the same memory buffer.
-	     Standard C requires that we always use the same type for them,
-	     so we must use the union type.  */
-	  inp = kbuf;
-	  outp = (void *) buf;
-
-	  while (&inp->b < &kbuf->b + retval)
-	    {
-	      const size_t alignment = __alignof__ (DIRENT_TYPE);
-	      /* Since inp->k.d_reclen is already aligned for the kernel
-		 structure this may compute a value that is bigger
-		 than necessary.  */
-	      size_t old_reclen = inp->k.d_reclen;
-	      size_t new_reclen = ((old_reclen - size_diff + alignment - 1)
-				  & ~(alignment - 1));
-
-	      /* Copy the data out of the old structure into temporary space.
-		 Then copy the name, which may overlap if BUF == KBUF.  */
-	      const uint64_t d_ino = inp->k.d_ino;
-	      const int64_t d_off = inp->k.d_off;
-	      const uint8_t d_type = inp->k.d_type;
-
-	      memmove (outp->u.d_name, inp->k.d_name,
-		       old_reclen - offsetof (struct kernel_dirent64, d_name));
-
-	      /* Now we have copied the data from INP and access only OUTP.  */
-
-	      DIRENT_SET_DP_INO (&outp->u, d_ino);
-	      outp->u.d_off = d_off;
-	      if ((sizeof (outp->u.d_ino) != sizeof (inp->k.d_ino)
-		   && outp->u.d_ino != d_ino)
-		  || (sizeof (outp->u.d_off) != sizeof (inp->k.d_off)
-		      && outp->u.d_off != d_off))
-		{
-		  /* Overflow.  If there was at least one entry
-		     before this one, return them without error,
-		     otherwise signal overflow.  */
-		  if (last_offset != -1)
-		    {
-		      __lseek64 (fd, last_offset, SEEK_SET);
-		      return outp->b - buf;
-		    }
-		  __set_errno (EOVERFLOW);
-		  return -1;
-		}
-
-	      last_offset = d_off;
-	      outp->u.d_reclen = new_reclen;
-	      outp->u.d_type = d_type;
-
-	      inp = (void *) inp + old_reclen;
-	      outp = (void *) outp + new_reclen;
-	    }
+	last_offset = d_off;
+	outp->u.d_reclen = new_reclen;
+	outp->u.d_type = d_type;
 
-	  return outp->b - buf;
-	}
+	inp = (void *) inp + old_reclen;
+	outp = (void *) outp + new_reclen;
+      }
 
-# ifndef __ASSUME_GETDENTS64_SYSCALL
-      __set_errno (saved_errno);
-      __have_no_getdents64 = 1;
-# endif
-    }
+    return outp->b - buf;
+  }
 #endif
   {
     size_t red_nbytes;