uClibc, libstdc++, and readdir()

Jeff Warren tiny.laser at comcast.net
Tue May 23 20:01:34 UTC 2006


Hi All,

I am having trouble with readdir and uClibc and thought I would see if
anyone here could help me.  The basic problem is that readdir will
return a dirent with d_type always set to zero.   
        Vapier committed a change on 2/3/2006 (Revision 13824) to
libc/sysdeps/linux/common/getdents.c and getdents64.c which fixes this.
I am running uClibc 0.9.26 on my target system, so I had to apply the
changes slightly differently so I didn't have to use the Large file
system flag.
        So I applied the fix and put the library in my root filesystem
for the
target and I see no change.
        I then look at my process and see with the gdb command 'info
sharedlibrary' that libc.so.0 is not even loaded.  And that sortof makes
sense to me as it is a C++ app and it has the libstdc++.so.5.0.6 and the
lduClibc.so loaded.  Here is where I get confused:  In a c++ app, does
it use the installed C library to run calls such as readdir()?  On my
target it doesn't seem so as I can delete uClibc from the /lib dir and
restart my application without problems.

        So if it doesn't get the functionality from the installed C
library,
where does it get it from, libstdc++?  I looked in there and I can't
seem to find readdir (arm-linux-readelf --syms libstdc++.so.5.0.6 | grep
readdir)

        By the way, I put a printk statement in both of sys_getdents and
sys_getdents64 in my kernel (2.6.12.1) and even with my changes, I see
that readdir() fires off the sys_getdents printk when it should fire off
the sys_getdents64 printk if the changes are correct.

I have made other changes to the uClibc, for instance specifically
setting the d_type, which still seems to have no effect.  I am
definitely missing some knowledge on what is going on.  Any guidance on
this would be appreciated.

Thanks in advance,

Regards,

Jeff Warren

P.S.  I have included the diff that containse the changes I've made
against 0.9.26.

 
-------------- next part --------------
--- libc/sysdeps/linux/common/getdents.c.orig	2003-02-10 13:15:20.000000000 -0800
+++ libc/sysdeps/linux/common/getdents.c	2006-05-22 10:52:32.000000000 -0700
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 #include <sysdep.h>
 #include <sys/syscall.h>
+#include <features.h>
 
 
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
@@ -37,64 +38,36 @@
     long		d_ino;
     __kernel_off_t	d_off;
     unsigned short	d_reclen;
-    char		d_name[256];
+    char		d_name[256];    
 };
 
 #define __NR___syscall_getdents __NR_getdents
 static inline _syscall3(int, __syscall_getdents, int, fd, unsigned char *, kdirp, size_t, count);
 
 
+extern ssize_t __getdents64 (int fd, char *buf, size_t nbytes); 
 ssize_t __getdents (int fd, char *buf, size_t nbytes)
 {
     struct dirent *dp;
-    off_t last_offset = -1;
-    ssize_t retval;
-    size_t red_nbytes;
-    struct kernel_dirent *skdp, *kdp;
-    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), 
-	    nbytes - size_diff);
-
-    dp = (struct dirent *) buf;
-    skdp = kdp = alloca (red_nbytes);
-
-    retval = __syscall_getdents(fd, (char *)kdp, red_nbytes);
-    if (retval == -1)
-	return -1;
-
-    while ((char *) kdp < (char *) skdp + retval) {
-	const size_t alignment = __alignof__ (struct dirent);
-	/* 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;
-	dp->d_ino = 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 = (struct dirent *) ((char *) dp + new_reclen);
-	kdp = (struct kernel_dirent *) (((char *) kdp) + kdp->d_reclen);
+    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 (char *) dp - buf;
+
+    return ret;
 }
+
--- libc/sysdeps/linux/common/getdents64.c.orig	2003-02-10 13:15:20.000000000 -0800
+++ libc/sysdeps/linux/common/getdents64.c	2006-05-23 11:53:31.000000000 -0700
@@ -30,7 +30,7 @@
 #include <sys/types.h>
 #include <sys/syscall.h>
 
-#if defined __UCLIBC_HAS_LFS__ && defined __NR_getdents64 
+//#if defined __NR_getdents64 
 
 
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
@@ -52,7 +52,7 @@
 ssize_t __getdents64 (int fd, char *buf, size_t nbytes)
 {
     struct dirent64 *dp;
-    off64_t last_offset = -1;
+    off_t last_offset = -1;
     ssize_t retval;
     size_t red_nbytes;
     struct kernel_dirent64 *skdp, *kdp;
@@ -80,7 +80,7 @@
 	    /* Our heuristic failed.  We read too many entries.  Reset
 	       the stream.  */
 	    assert (last_offset != -1);
-	    lseek64(fd, last_offset, SEEK_SET);
+	    lseek(fd, last_offset, SEEK_SET);
 
 	    if ((char *) dp == buf) {
 		/* The buffer the user passed in is too small to hold even
@@ -95,7 +95,7 @@
 	dp->d_ino = kdp->d_ino;
 	dp->d_off = kdp->d_off;
 	dp->d_reclen = new_reclen;
-	dp->d_type = DT_UNKNOWN;
+	dp->d_type = kdp->d_type;
 	memcpy (dp->d_name, kdp->d_name,
 		kdp->d_reclen - offsetof (struct kernel_dirent64, d_name));
 	dp = (struct dirent64 *) ((char *) dp + new_reclen);
@@ -103,11 +103,11 @@
     }
     return (char *) dp - buf;
 }
-#else
+/*#else
 ssize_t __getdents (int fd, char *buf, size_t nbytes);
 ssize_t __getdents64 (int fd, char *buf, size_t nbytes)
 {
     return(__getdents(fd, buf, nbytes));
 }
-#endif /* __UCLIBC_HAS_LFS__ */
+#endif*/ /* __UCLIBC_HAS_LFS__ */
 
--- libc/sysdeps/linux/common/bits/dirent.h.orig	2006-05-23 12:38:55.000000000 -0700
+++ libc/sysdeps/linux/common/bits/dirent.h	2006-05-23 12:39:14.000000000 -0700
@@ -34,7 +34,7 @@
     char d_name[256];		/* We must not include limits.h! */
   };
 
-#ifdef __USE_LARGEFILE64
+//#ifdef __USE_LARGEFILE64
 struct dirent64
   {
     __ino64_t d_ino;
@@ -43,7 +43,7 @@
     unsigned char d_type;
     char d_name[256];		/* We must not include limits.h! */
   };
-#endif
+//#endif
 
 #define d_fileno	d_ino	/* Backwards compatibility.  */
 


More information about the uClibc mailing list