aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog19
-rw-r--r--sysdeps/unix/sysv/linux/alpha/getdents.c1
-rw-r--r--sysdeps/unix/sysv/linux/arm/Versions15
-rw-r--r--sysdeps/unix/sysv/linux/getdents.c212
-rw-r--r--sysdeps/unix/sysv/linux/i386/Versions2
-rw-r--r--sysdeps/unix/sysv/linux/i386/getdents64.c1
-rw-r--r--sysdeps/unix/sysv/linux/kernel-features.h6
-rw-r--r--sysdeps/unix/sysv/linux/powerpc/Versions8
-rw-r--r--sysdeps/unix/sysv/linux/sparc/sparc32/Versions9
9 files changed, 206 insertions, 67 deletions
diff --git a/ChangeLog b/ChangeLog
index 6a4bf52..c552bc4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2000-08-14 Jakub Jelinek <jakub@redhat.com>
+
+ * dirent/Versions (getdirentries64): Export at GLIBC_2.2.
+ * sysdeps/unix/sysv/linux/kernel-features.h
+ (__ASSUME_GETDENTS64_SYSCALL): Define.
+ * sysdeps/unix/sysv/linux/getdents.c (__getdents): Use getdents64
+ syscall if available to get d_type fields.
+ * sysdeps/unix/sysv/linux/alpha/getdents.c (DIRENT_TYPE): Define.
+ * sysdeps/unix/sysv/linux/arm/Versions (__xstat64, __fxstat64,
+ __lxstat64): Export at GLIBC_2.2.
+ (alphasort64, readdir64, readdir64_r, scandir64, versionsort64):
+ Likewise.
+ * sysdeps/unix/sysv/linux/i386/Versions (getdirentries64): Remove.
+ * sysdeps/unix/sysv/linux/i386/getdents64.c (kernel_dirent64): Define.
+ * sysdeps/unix/sysv/linux/powerpc/Versions (alphasort64,
+ getdirentries64, versionsort64): Remove.
+ * sysdeps/unix/sysv/linux/sparc/sparc32/Versions (alphasort64,
+ getdirentries64, versionsort64): Remove.
+
2000-08-13 Ulrich Drepper <drepper@redhat.com>
* posix/Makefile: Remove rules to generate glob package.
diff --git a/sysdeps/unix/sysv/linux/alpha/getdents.c b/sysdeps/unix/sysv/linux/alpha/getdents.c
index 6deb87e..175be9d 100644
--- a/sysdeps/unix/sysv/linux/alpha/getdents.c
+++ b/sysdeps/unix/sysv/linux/alpha/getdents.c
@@ -1,3 +1,4 @@
+#define DIRENT_TYPE struct dirent64
#define DIRENT_SET_DP_INO(dp, value) \
do { (dp)->d_ino = (value); (dp)->__pad = 0; } while (0)
#define __getdents64 __no___getdents64_decl
diff --git a/sysdeps/unix/sysv/linux/arm/Versions b/sysdeps/unix/sysv/linux/arm/Versions
index 4ac5b58..5498086 100644
--- a/sysdeps/unix/sysv/linux/arm/Versions
+++ b/sysdeps/unix/sysv/linux/arm/Versions
@@ -11,7 +11,22 @@ libc {
outb; outw; outl;
}
GLIBC_2.2 {
+ # functions used in other libraries
+ __xstat64; __fxstat64; __lxstat64;
+
+ # a*
+ alphasort64;
+
# New rlimit interface
getrlimit; setrlimit; getrlimit64;
+
+ # r*
+ readdir64; readdir64_r;
+
+ # s*
+ scandir64;
+
+ # v*
+ versionsort64;
}
}
diff --git a/sysdeps/unix/sysv/linux/getdents.c b/sysdeps/unix/sysv/linux/getdents.c
index 474bf19..19ab923 100644
--- a/sysdeps/unix/sysv/linux/getdents.c
+++ b/sysdeps/unix/sysv/linux/getdents.c
@@ -32,6 +32,19 @@
#include <linux/posix_types.h>
+#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;
+#else
+extern int __have_no_getdents64;
+#endif
+#endif
+#endif
+
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
extern int __syscall_getdents (int fd, char *__unbounded buf, size_t nbytes);
@@ -51,8 +64,19 @@ struct kernel_dirent
char d_name[256];
};
+struct kernel_dirent64
+ {
+ u_int64_t d_ino;
+ int64_t d_off;
+ unsigned short int d_reclen;
+ unsigned char d_type;
+ char d_name[256];
+ };
+
#ifndef __GETDENTS
# define __GETDENTS __getdents
+#endif
+#ifndef DIRENT_TYPE
# define DIRENT_TYPE struct dirent
#endif
#ifndef DIRENT_SET_DP_INO
@@ -71,63 +95,155 @@ ssize_t
internal_function
__GETDENTS (int fd, char *buf, size_t nbytes)
{
- off_t last_offset = -1;
- size_t red_nbytes;
- struct kernel_dirent *skdp, *kdp;
DIRENT_TYPE *dp;
- int retval;
- const size_t size_diff = (offsetof (DIRENT_TYPE, d_name)
- - offsetof (struct kernel_dirent, d_name));
-
- red_nbytes = MIN (nbytes
- - ((nbytes / (offsetof (DIRENT_TYPE, d_name) + 14))
- * size_diff),
- nbytes - size_diff);
-
- dp = (DIRENT_TYPE *) buf;
- skdp = kdp = __alloca (red_nbytes);
-
- retval = INLINE_SYSCALL (getdents, 3, fd,
- CHECK_N ((char *) kdp, red_nbytes), red_nbytes);
-
- if (retval == -1)
- return -1;
+ off_t last_offset = -1;
+ ssize_t retval;
- while ((char *) kdp < (char *) skdp + retval)
+#ifdef __NR_getdents64
+#ifndef __ASSUME_GETDENTS64_SYSCALL
+ if (!__have_no_getdents64)
+#endif
{
- const size_t alignment = __alignof__ (DIRENT_TYPE);
- /* Since kdp->d_reclen is already aligned for the kernel structure
- this may compute a value that is bigger than necessary. */
- size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
- & ~(alignment - 1));
- if ((char *) dp + new_reclen > buf + nbytes)
+#ifndef __ASSUME_GETDENTS64_SYSCALL
+ int saved_errno = errno;
+#endif
+ char *kbuf = buf;
+ size_t kbytes = nbytes;
+ if (offsetof (DIRENT_TYPE, d_name)
+ < offsetof (struct kernel_dirent64, d_name)
+ && nbytes <= sizeof (DIRENT_TYPE))
{
- /* Our heuristic failed. We read too many entries. Reset
- the stream. */
- assert (last_offset != -1);
- __lseek (fd, last_offset, SEEK_SET);
-
- if ((char *) dp == buf)
+ kbytes = nbytes + offsetof (struct kernel_dirent64, d_name)
+ - offsetof (DIRENT_TYPE, d_name);
+ kbuf = __alloca(kbytes);
+ }
+ retval = INLINE_SYSCALL (getdents64, 3, fd, CHECK_N(kbuf, kbytes),
+ kbytes);
+#ifndef __ASSUME_GETDENTS64_SYSCALL
+ if (retval != -1 && errno != -EINVAL)
+#endif
+ {
+ struct kernel_dirent64 *kdp;
+ const size_t size_diff = (offsetof (struct kernel_dirent64, d_name)
+ - offsetof (DIRENT_TYPE, d_name));
+
+ /* 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 (dp->d_ino) == sizeof (kdp->d_ino)
+ && sizeof (dp->d_off) == sizeof (kdp->d_off))
+ return retval;
+
+ dp = (DIRENT_TYPE *)buf;
+ kdp = (struct kernel_dirent64 *)kbuf;
+ while ((char *) kdp < kbuf + retval)
{
- /* The buffer the user passed in is too small to hold even
- one entry. */
- __set_errno (EINVAL);
- return -1;
+ const size_t alignment = __alignof__ (DIRENT_TYPE);
+ /* Since kdp->d_reclen is already aligned for the kernel
+ structure this may compute a value that is bigger
+ than necessary. */
+ size_t old_reclen = kdp->d_reclen;
+ size_t new_reclen = ((old_reclen - size_diff + alignment - 1)
+ & ~(alignment - 1));
+ u_int64_t d_ino = kdp->d_ino;
+ int64_t d_off = kdp->d_off;
+ unsigned char d_type = kdp->d_type;
+
+ DIRENT_SET_DP_INO(dp, d_ino);
+ dp->d_off = d_off;
+ if ((sizeof (dp->d_ino) != sizeof (kdp->d_ino)
+ && dp->d_ino != d_ino)
+ || (sizeof (dp->d_off) != sizeof (kdp->d_off)
+ && dp->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)
+ {
+ __lseek (fd, last_offset, SEEK_SET);
+ return (char *) dp - buf;
+ }
+ __set_errno (EOVERFLOW);
+ return -1;
+ }
+
+ last_offset = d_off;
+ dp->d_reclen = new_reclen;
+ dp->d_type = d_type;
+ memmove (dp->d_name, kdp->d_name,
+ old_reclen - offsetof (struct kernel_dirent64, d_name));
+
+ dp = (DIRENT_TYPE *) ((char *) dp + new_reclen);
+ kdp = (struct kernel_dirent64 *) ((char *) kdp + old_reclen);
}
- break;
+ return (char *) dp - buf;
}
- last_offset = kdp->d_off;
- DIRENT_SET_DP_INO(dp, kdp->d_ino);
- dp->d_off = kdp->d_off;
- dp->d_reclen = new_reclen;
- dp->d_type = DT_UNKNOWN;
- memcpy (dp->d_name, kdp->d_name,
- kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
-
- dp = (DIRENT_TYPE *) ((char *) dp + new_reclen);
- kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
+#ifndef __ASSUME_GETDENTS64_SYSCALL
+ __set_errno (saved_errno);
+ __have_no_getdents64 = 1;
+#endif
+ }
+#endif
+ {
+ size_t red_nbytes;
+ struct kernel_dirent *skdp, *kdp;
+ const size_t size_diff = (offsetof (DIRENT_TYPE, d_name)
+ - offsetof (struct kernel_dirent, d_name));
+
+ red_nbytes = MIN (nbytes
+ - ((nbytes / (offsetof (DIRENT_TYPE, d_name) + 14))
+ * size_diff),
+ nbytes - size_diff);
+
+ dp = (DIRENT_TYPE *) buf;
+ skdp = kdp = __alloca (red_nbytes);
+
+ retval = INLINE_SYSCALL (getdents, 3, fd,
+ CHECK_N ((char *) kdp, red_nbytes), red_nbytes);
+
+ if (retval == -1)
+ return -1;
+
+ while ((char *) kdp < (char *) skdp + retval)
+ {
+ const size_t alignment = __alignof__ (DIRENT_TYPE);
+ /* Since kdp->d_reclen is already aligned for the kernel structure
+ this may compute a value that is bigger than necessary. */
+ size_t new_reclen = ((kdp->d_reclen + size_diff + alignment - 1)
+ & ~(alignment - 1));
+ if ((char *) dp + new_reclen > buf + nbytes)
+ {
+ /* Our heuristic failed. We read too many entries. Reset
+ the stream. */
+ assert (last_offset != -1);
+ __lseek (fd, last_offset, SEEK_SET);
+
+ if ((char *) dp == buf)
+ {
+ /* The buffer the user passed in is too small to hold even
+ one entry. */
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ break;
+ }
+
+ last_offset = kdp->d_off;
+ DIRENT_SET_DP_INO(dp, kdp->d_ino);
+ dp->d_off = kdp->d_off;
+ dp->d_reclen = new_reclen;
+ dp->d_type = DT_UNKNOWN;
+ memcpy (dp->d_name, kdp->d_name,
+ kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
+
+ dp = (DIRENT_TYPE *) ((char *) dp + new_reclen);
+ kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
+ }
}
return (char *) dp - buf;
diff --git a/sysdeps/unix/sysv/linux/i386/Versions b/sysdeps/unix/sysv/linux/i386/Versions
index 58c7b3d..b7af24b 100644
--- a/sysdeps/unix/sysv/linux/i386/Versions
+++ b/sysdeps/unix/sysv/linux/i386/Versions
@@ -19,8 +19,6 @@ libc {
# a*
alphasort64;
- # g*
- getdirentries64;
# New rlimit interface
getrlimit; setrlimit; getrlimit64;
diff --git a/sysdeps/unix/sysv/linux/i386/getdents64.c b/sysdeps/unix/sysv/linux/i386/getdents64.c
index dac046f..bbfff20 100644
--- a/sysdeps/unix/sysv/linux/i386/getdents64.c
+++ b/sysdeps/unix/sysv/linux/i386/getdents64.c
@@ -36,6 +36,7 @@ versioned_symbol (libc, __getdents64, getdents64, GLIBC_2_2);
#define __GETDENTS __old_getdents64
#define DIRENT_TYPE struct __old_dirent64
#define kernel_dirent old_kernel_dirent
+#define kernel_dirent64 old_kernel_dirent64
#include <sysdeps/unix/sysv/linux/getdents.c>
diff --git a/sysdeps/unix/sysv/linux/kernel-features.h b/sysdeps/unix/sysv/linux/kernel-features.h
index e7699a6..2f9d12b 100644
--- a/sysdeps/unix/sysv/linux/kernel-features.h
+++ b/sysdeps/unix/sysv/linux/kernel-features.h
@@ -136,3 +136,9 @@
#if __LINUX_KERNEL_VERSION >= 132097 && (defined __i386__ || defined __sparc__)
# define __ASSUME_FCNTL64 1
#endif
+
+/* The getdents64 syscall was introduced in 2.4.0-test7. We test for
+ 2.4.1 for the earliest version we know the syscall is available. */
+#if __LINUX_KERNEL_VERSION >= 132097
+# define __ASSUME_GETDENTS64_SYSCALL 1
+#endif
diff --git a/sysdeps/unix/sysv/linux/powerpc/Versions b/sysdeps/unix/sysv/linux/powerpc/Versions
index 6466be2..1ea93d7 100644
--- a/sysdeps/unix/sysv/linux/powerpc/Versions
+++ b/sysdeps/unix/sysv/linux/powerpc/Versions
@@ -9,11 +9,6 @@ libc {
# functions used in other libraries
__xstat64; __fxstat64; __lxstat64;
- # a*
- alphasort64;
-
- # g*
- getdirentries64;
# New rlimit interface
getrlimit; setrlimit; getrlimit64; setrlimit64;
@@ -22,8 +17,5 @@ libc {
# s*
scandir64;
-
- # v*
- versionsort64;
}
}
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/Versions b/sysdeps/unix/sysv/linux/sparc/sparc32/Versions
index fcb9df3..2448fa2 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/Versions
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/Versions
@@ -9,19 +9,10 @@ libc {
# functions used in other libraries
__xstat64; __fxstat64; __lxstat64;
- # a*
- alphasort64;
-
- # g*
- getdirentries64;
-
# r*
readdir64; readdir64_r;
# s*
scandir64;
-
- # v*
- versionsort64;
}
}