Can telldir() == 0 value be made special?

Conrad Meyer cem at freebsd.org
Tue Aug 22 14:42:45 UTC 2017


Hi Nikolaus,

As you have surmised, DIR* seekpoints are created dynamically whenever
requested by user's telldir() call:

https://github.com/freebsd/freebsd/blob/master/lib/libc/gen/telldir.c#L53

I believe we could special case zero without breaking ABI
compatibility of correct programs.  Something like this:

--- a/lib/libc/gen/telldir.c
+++ b/lib/libc/gen/telldir.c
@@ -70,6 +70,20 @@ telldir(DIR *dirp)
                }
        }
        if (lp == NULL) {
+               /* Create special zero telldir entry, similar to Linux */
+               if (dirp->dd_td->td_loccnt == 0 && dirp->dd_loc != 0) {
+                       lp = malloc(sizeof(struct ddloc));
+                       if (lp == NULL) {
+                               if (__isthreaded)
+                                       _pthread_mutex_unlock(&dirp->dd_lock);
+                               return (-1);
+                       }
+                       lp->loc_index = dirp->dd_td->td_loccnt++;
+                       lp->loc_seek = 0;
+                       lp->loc_loc = 0;
+                       LIST_INSERT_HEAD(&dirp->dd_td->td_locq, lp, loc_lqe);
+               }
+
                lp = malloc(sizeof(struct ddloc));
                if (lp == NULL) {
                        if (__isthreaded)

I don't know if there are any downsides to special-casing zero like
this, other than additional code complexity.

Best,
Conrad

On Tue, Aug 22, 2017 at 4:38 AM, Nikolaus Rath <Nikolaus at rath.org> wrote:
> Hello,
>
> I am trying to debug a test failure of libfuse under FreeBSD. I believe
> I have reduced it to the following root cause:
>
> Consider the following program:
>
> #include <stdio.h>
> #include <dirent.h>
> #include <sys/types.h>
>
> int main(void) {
>     struct dirent *e = NULL;
>     DIR *dirp;
>
>     printf("opendir...\n");
>     dirp = opendir("/");
>     if(dirp == NULL) {
>         perror("opendir");
>         return 1;
>     }
>     printf("telldir: %ld\n", telldir(dirp));
>     e = readdir(dirp);
>     printf("readdir: %s\n", e->d_name);
>     printf("telldir: %ld\n", telldir(dirp));
>     e = readdir(dirp);
>     printf("readdir: %s\n", e->d_name);
>     printf("closedir..\n");
>     closedir(dirp);
>
>     printf("opendir...\n");
>     dirp = opendir("/");
>     if(dirp == NULL) {
>         perror("opendir");
>         return 1;
>     }
>     e = readdir(dirp);
>     printf("readdir: %s\n", e->d_name);
>     printf("telldir: %ld\n", telldir(dirp));
>     e = readdir(dirp);
>     printf("readdir: %s\n", e->d_name);
>     printf("closedir..\n");
>     closedir(dirp);
>
>     return 0;
> }
>
>
> Under FreeBSD, running it gives:
>
> # ./simple
> opendir...
> telldir: 0
> readdir: .
> telldir: 1
> readdir: ..
> closedir..
> opendir...
> readdir: .
> telldir: 0
> readdir: ..
> closedir..
>
> In other words, if telldir() is called right after opendir(), it gives
> an offset of zero. But if telldir() is called only after the first
> readdir() call, it also gives an offset of zero. My hypothesis is that
> FreeBSD actually just enumerates the different telldir() calls - is that
> correct?
>
> Now, the offsets returned by telldir() are documented to be valid only
> within a given *dirp, so FreeBSD isn't doing anything wrong.
>
> However, having different meanings even for an offset of zero causes
> problems for libfuse, because under Linux an offset of zero is
> guaranteed to mean "first entry". This is reflected in the definition of
> the fuse readdir() function which always receives an *offset* parameter
> that needs to have a definite value even when telldir() was never
> called. If zero is suddenly also a valid telldir() return value that may
> indicate some other position in the stream, things get complicated.
>
> Now, I think I managed to work around that by shifting all offsets by
> one, but that is awkward (and I may have overlooked some problems that
> the unit tests don't cover). So I am wondering:
>
> Is there a reason why the FreeBSD kernel could not start enumerating
> telldir() offsets with 1, so that 0 can always have the same meaning?
>
>
> Best,
> -Nikolaus
>
> --
> GPG Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F
>
>              »Time flies like an arrow, fruit flies like a Banana.«
> _______________________________________________
> freebsd-fs at freebsd.org mailing list
> https://lists.freebsd.org/mailman/listinfo/freebsd-fs
> To unsubscribe, send any mail to "freebsd-fs-unsubscribe at freebsd.org"


More information about the freebsd-fs mailing list