readdir/telldir/seekdir problem (i think)

Julian Elischer julian at freebsd.org
Mon Apr 27 16:44:49 UTC 2015


On 4/25/15 4:28 AM, John Baldwin wrote:
> On Saturday, April 25, 2015 02:36:24 AM Julian Elischer wrote:
>> On 4/25/15 1:30 AM, Julian Elischer wrote:
>>> On 4/24/15 10:59 PM, John Baldwin wrote:
>>>> Index: head/lib/libc/gen/telldir.c
>>>> ===================================================================
>>>> --- head/lib/libc/gen/telldir.c (revision 281929)
>>>> +++ head/lib/libc/gen/telldir.c (working copy)
>>>> @@ -101,8 +101,10 @@
>>>>                   return;
>>>>           if (lp->loc_loc == dirp->dd_loc && lp->loc_seek ==
>>>> dirp->dd_seek)
>>>>                   return;
>>>> -       (void) lseek(dirp->dd_fd, (off_t)lp->loc_seek, SEEK_SET);
>>>> -       dirp->dd_seek = lp->loc_seek;
>>>> +       if (lp->loc_seek != dirp->dd_seek) {
>>>> +               (void) lseek(dirp->dd_fd, (off_t)lp->loc_seek,
>>>> SEEK_SET);
>>>> +               dirp->dd_seek = lp->loc_seek;
>>>> +       }
>>> yes I did that yesterday but it still fails when you transition
>>> blocks.. (badly).
>>>
>>> I also tried bigger blocks.. also fails (eventually)
>>>
>>> I did find a way to make it work...  you had to seek back
>>> to the first block you deleted on each set..
>>> then work forward from there again..  unfortunately since
>>> I'm trying to make a microsoft program not fail (via samba)
>>> I have no control over how it does things and seekdir doesn't
>>> know what was deleted anyway... (so the fix is fine for  the
>>> test program but not for real life)
>>>
>>> I think I can make the BSD one act like the linux one by changing
>>> the lseek being done to use the offset (loc) plus the buffer seek
>>> address of the target, instead of just going for the buffer base and
>>> stepping forward through the entries..
>>>
>>> maybe tomorrow.
>>>
>> The following conditional code makes ours behave the same as the linux
>> one.
>> it breaks several 'rules' but works where ours is clean but fails..
>> as Rick said..  "maybe that's what we should do too."
>>
>>
>> this is at the end of seekdir()
>>
>>
>> The new code does what linux does.. and shouldn't work.. but does
>>               // at least in the limited conditions I need it to.
>>               // We'll probably need to do this at work...:
>>
>>
>> The original code is what we have now, but gets mightily confused
>> sometimes.
>>          // This is clean(er) but fails in specific situations(when
>> doing commands
>>          // from Microft windows, via samba).
>>
>>
>> root at vps1:/tmp # diff -u dir.c.orig dir.c
>> --- dir.c.orig    2015-04-24 11:29:36.855317000 -0700
>> +++ dir.c    2015-04-24 11:15:49.058500000 -0700
>> @@ -1105,6 +1105,13 @@
>>            dirp->dd_loc = lp->loc_loc;
>>            return;
>>        }
>> +#ifdef GLIBC_SEEK
>> +    (void) lseek(dirp->dd_fd, (off_t)lp->loc_seek + lp->loc_loc,
>> SEEK_SET);
>> +    dirp->dd_seek = lp->loc_seek + lp->loc_loc;
>> +    dirp->dd_loc = 0;
>> +    lp->loc_seek = dirp->dd_seek;
>> +    lp->loc_loc = 0;
>> +#else
>>        (void) lseek(dirp->dd_fd, (off_t)lp->loc_seek, SEEK_SET);
>>        dirp->dd_seek = lp->loc_seek;
>>        dirp->dd_loc = 0;
>> @@ -1114,6 +1121,7 @@
>>            if (dp == NULL)
>>                break;
>>        }
>> +#endif
>>    }
> Yes, this isn't at all safe.  There's no guarantee whatsoever that
> the offset on the directory fd that isn't something returned by
> getdirentries has any meaning.  In particular, the size of the
> directory entry in a random filesystem might be a different size
> than the structure returned by getdirentries (since it converts
> things into a FS-independent format).
>
> This might work for UFS by accident, but this is probably why ZFS
> doesn't work.
>
> However, this might be properly fixed by the thing that ino64 is
> doing where each directory entry returned by getdirentries gives
> you a seek offset that you _can_ directly seek to (as opposed to
> seeking to the start of the block and then walking forward N
> entries until you get an inter-block entry that is the same).
I just made the stunning discovery that our seekdir/readdir/telldir 
code in libc works with
FreeBSD 8.0.
so maybe the problem is that the kernel changed it's behaviour, and 
no-one thought to fix libc..

(at least it works on one of our 8.0 base appliances.. I'll do more 
testing tomorrow.. it's past midnight.)



>



More information about the freebsd-current mailing list