summaryrefslogtreecommitdiff
path: root/sysdeps/unix/sysv/linux
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/sysv/linux')
-rw-r--r--sysdeps/unix/sysv/linux/getdents.c191
-rw-r--r--sysdeps/unix/sysv/linux/kernel-features.h4
2 files changed, 81 insertions, 114 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;
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index 28195e5629..5a1b204033 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -48,10 +48,6 @@
and still does not have a 64-bit inode field. */
#define __ASSUME_ST_INO_64_BIT 1
-/* The getdents64 syscall was introduced in 2.4.0-test7 (but later for
- MIPS n32). */
-#define __ASSUME_GETDENTS64_SYSCALL 1
-
/* The statfs64 syscalls are available in 2.5.74 (but not for alpha). */
#define __ASSUME_STATFS64 1