OSDN Git Service

Use getdents syscall if kernel provide supports for this
[uclinux-h8/uClibc.git] / libc / sysdeps / linux / common / getdents.c
index 24ce2c8..97c6d8b 100644 (file)
@@ -1,20 +1,8 @@
-/* Copyright (C) 1993, 1995-2002 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+/*
+ * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
+ */
 
 #include <alloca.h>
 #include <assert.h>
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/syscall.h>
+#include <bits/kernel_types.h>
+
+/* With newer versions of linux, the getdents syscall returns d_type
+ * information after the name field.
+ *
+ * See __ASSUME_GETDENTS32_D_TYPE in glibc's kernel-features.h for specific
+ * version / arch details.
+ */
 
-#undef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#ifndef offsetof
+# define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
 
 struct kernel_dirent
 {
-    long               d_ino;
-    __kernel_off_t     d_off;
-    unsigned short     d_reclen;
-    char               d_name[256];
+       long int d_ino;
+       __kernel_off_t d_off;
+       unsigned short int d_reclen;
+       char d_name[256];
 };
 
+ssize_t __getdents (int fd, char *buf, size_t nbytes) attribute_hidden;
+
 #define __NR___syscall_getdents __NR_getdents
 static inline _syscall3(int, __syscall_getdents, int, fd, unsigned char *, kdirp, size_t, count);
 
+#ifdef __ASSUME_GETDENTS32_D_TYPE
+ssize_t __getdents (int fd, char *buf, size_t nbytes)
+{
+       ssize_t retval;
+
+       retval = __syscall_getdents(fd, (unsigned char *)buf, nbytes);
+
+       /* The kernel added the d_type value after the name.  Change
+       this now.  */
+       if (retval != -1) {
+               union {
+                       struct kernel_dirent k;
+                       struct dirent u;
+               } *kbuf = (void *) buf;
+
+               while ((char *) kbuf < buf + retval) {
+                       char d_type = *((char *) kbuf + kbuf->k.d_reclen - 1);
+                       memmove (kbuf->u.d_name, kbuf->k.d_name,
+                       strlen (kbuf->k.d_name) + 1);
+                       kbuf->u.d_type = d_type;
+
+                       kbuf = (void *) ((char *) kbuf + kbuf->k.d_reclen);
+               }
+       }
+
+       return retval;
+}
+
+#elif ! defined __UCLIBC_HAS_LFS__ || ! defined __NR_getdents64
 
-ssize_t attribute_hidden __getdents (int fd, char *buf, size_t nbytes)
+/* Experimentally off - libc_hidden_proto(memcpy) */
+libc_hidden_proto(lseek)
+
+ssize_t __getdents (int fd, char *buf, size_t nbytes)
 {
     struct dirent *dp;
     off_t last_offset = -1;
@@ -53,14 +84,14 @@ ssize_t attribute_hidden __getdents (int fd, char *buf, size_t nbytes)
     const size_t size_diff = (offsetof (struct dirent, d_name)
            - offsetof (struct kernel_dirent, d_name));
 
-    red_nbytes = MIN (nbytes - ((nbytes / 
-                   (offsetof (struct dirent, d_name) + 14)) * size_diff), 
+    red_nbytes = MIN (nbytes - ((nbytes /
+                   (offsetof (struct dirent, d_name) + 14)) * size_diff),
            nbytes - size_diff);
 
     dp = (struct dirent *) buf;
     skdp = kdp = alloca (red_nbytes);
 
-    retval = __syscall_getdents(fd, (char *)kdp, red_nbytes);
+    retval = __syscall_getdents(fd, (unsigned char *)kdp, red_nbytes);
     if (retval == -1)
        return -1;
 
@@ -90,10 +121,46 @@ ssize_t attribute_hidden __getdents (int fd, char *buf, size_t nbytes)
        dp->d_off = kdp->d_off;
        dp->d_reclen = new_reclen;
        dp->d_type = DT_UNKNOWN;
-       __memcpy (dp->d_name, kdp->d_name,
+       memcpy (dp->d_name, kdp->d_name,
                kdp->d_reclen - offsetof (struct kernel_dirent, d_name));
        dp = (struct dirent *) ((char *) dp + new_reclen);
        kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
     }
     return (char *) dp - buf;
 }
+
+#if defined __UCLIBC_HAS_LFS__ && ! defined __NR_getdents64
+attribute_hidden strong_alias(__getdents,__getdents64)
+#endif
+
+#elif __WORDSIZE == 32
+
+/* Experimentally off - libc_hidden_proto(memmove) */
+
+extern __typeof(__getdents) __getdents64 attribute_hidden;
+ssize_t __getdents (int fd, char *buf, size_t nbytes)
+{
+    struct dirent *dp;
+    struct dirent64 *dp64;
+    ssize_t ret = __getdents64 (fd, buf, nbytes);
+
+    if (ret <= 0)
+       return ret;
+
+    dp64 = (struct dirent64 *) buf;
+    buf += ret;
+    while ((void *) dp64 < (void *) buf) {
+       dp = (struct dirent *) dp64;
+       dp->d_ino = dp64->d_ino;
+       dp->d_off = dp64->d_off;
+       dp->d_reclen = dp64->d_reclen;
+       dp->d_type = dp64->d_type;
+       memmove (dp->d_name, dp64->d_name, dp->d_reclen - offsetof (struct dirent64, d_name));
+       memmove (dp64, dp, dp->d_reclen);
+       dp64 = ((void *) dp64) + dp->d_reclen;
+    }
+
+    return ret;
+}
+
+#endif