Subject: should we fix Linux emulation of getdents?
To: None <tech-kern@netbsd.org>
From: Emmanuel Dreyfus <p99dreyf@criens.u-psud.fr>
List: tech-kern
Date: 02/16/2001 21:00:15
I fixed a bug in the way we emulated a bug in Linux (Linux fixed the
bug, and we end up with a bug which is not an emulated bug), and I
wonder if it is worth a commit. It is currently guarded
(LINUX_NO_GETDENTS_BUG), I activate this macro through arch dependent
linux_types.h

It makes emulated getdents much closer to Linux's getdents. The
following inconsitencies are fixed:

1) NetBSD emulated getdents has the d_off field of returned structure
that points to itself, Linux points to the next field entry.

2) NetBSD emulated getdents sets the file offset to a given value, Linux
sets it to that value plus 8 bytes

3) NetBSD emulated getdents sets the last entry d_off to a virtual next
entry, Linux sets it to the end of the buffer.

I made it in order to fix a real problem, but it did not help on that
problem. I cannot claim it fixes anything, but it just make the emulated
getdents closer to the Linux getdents. I was not able to find who wrote
the code for the emulated getdents, so if this person is reading
tech-kern, please answer!

Here is the patch and a test program that helps checking what is
changing (compile it on Linux, run the binary on Linux, NetBSD without
the patch, and NetBSD with the patch, you will see the difference)

Index: linux_misc.c
===================================================================
RCS file: /cvsroot/syssrc/sys/compat/linux/common/linux_misc.c,v
retrieving revision 1.83
diff -r1.83 linux_misc.c
669a670,672
> #ifdef LINUX_NO_GETDENTS_BUG
>               idb.d_off = idb.d_off + linux_reclen - sizeof (idb.d_ino);
> #endif
685a689,691
> #ifdef LINUX_NO_GETDENTS_BUG
>       fp->f_offset = fp->f_offset + nbytes; 
> #else
686a693
> #endif
691a699,709
> #ifdef LINUX_NO_GETDENTS_BUG
>       /*
>        * Redo the last entry with a d_off 
>        * pointing to the end of the buffer
>        */
>       idb.d_off = fp->f_offset;
>       outp -= linux_reclen;
>       if ((error = copyout((caddr_t)&idb, outp, linux_reclen)))
>               goto out;
>       *retval = nbytes - resid - sizeof (idb.d_ino) - sizeof (idb.d_off);
> #else
692a711
> #endif

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#ifdef __NetBSD__
#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
#endif /* __NetBSD__ */

#ifdef __linux__
#include <linux/types.h>
#include <linux/dirent.h>
#include <linux/unistd.h>

_syscall3(int, getdents, uint, fd, struct dirent *, dirp, uint, count);
#endif /* __linux__ */

#define BUF_SIZE 4096

int main (int argc, char** argv) {
        int j,fd, res;
        struct dirent* entry;
        char buf[BUF_SIZE];
        size_t nbytes;
        off_t cur;
        char* filename = ".";

        if (argc > 1) 
                filename = argv[1];

        fd = open (filename, O_RDONLY, 0);
        if (fd == -1) {
                perror ("open() failed");
                exit (-1);
        }

        nbytes=BUF_SIZE;

        res = getdents (fd, buf, nbytes);
        if (res == -1) {
                perror ("getdents() failed");
                exit (-1);
        }
        
        cur = lseek (fd, 0, SEEK_CUR);
        if (cur==(off_t)-1) {
                perror ("lseek() failed\n");
                exit (-1);
        }

        printf ("fd=%d\n", fd);
        printf ("res=%d\n", res);
        printf ("offset=%d\n", cur);

        entry = (struct dirent *)buf;
   while (entry->d_reclen != 0) {
#ifdef __linux__
                printf ("d_ino=0x%x\n", entry->d_ino);
                printf ("d_off=0x%x\n", entry->d_off);
                printf ("d_reclen=0x%x\n", entry->d_reclen);
                printf ("d_name=%s\n", &entry->d_name);
#endif __linux__
#ifdef __NetBSD__
                printf ("d_fileno=0x%x\n", entry->d_fileno);
                printf ("d_reclen=0x%x\n", entry->d_reclen);
                printf ("d_namlen=0x%x\n", entry->d_namlen);
                printf ("d_name=%s\n", &entry->d_name);
#endif __NetBSD__
                printf ("\n");
                entry = (struct dirent *)((long)entry +
(long)(entry->d_reclen));
        }
}

-- 
Emmanuel Dreyfus.
"Le 80x86 n'est pas si complexe - il n'a simplement pas de sens"
(Mike Johnson, responsable de la conception x86 chez AMD) 
p99dreyf@criens.u-psud.fr