Page MenuHomeFreeBSD

Make rewinddir() always rewind.
ClosedPublic

Authored by jhb on Jun 30 2014, 7:07 PM.
Tags
None
Referenced Files
Unknown Object (File)
Sep 26 2024, 3:25 PM
Unknown Object (File)
Jul 7 2024, 11:12 PM
Unknown Object (File)
Jul 4 2024, 12:35 AM
Unknown Object (File)
May 10 2024, 2:56 AM
Unknown Object (File)
May 10 2024, 2:56 AM
Unknown Object (File)
May 10 2024, 2:56 AM
Unknown Object (File)
May 10 2024, 1:03 AM
Unknown Object (File)
May 9 2024, 9:20 PM
Subscribers
None

Details

Reviewers
jilles
Summary

Fix some edge cases with rewinddir():

  • In the unionfs case, opendir() and fdopendir() read the directory's full contents and cache it. This cache is not refreshed when rewinddir() is called, so rewinddir() will not notice updates to a directory. Fix this by splitting the code to fetch a directory's contents out of __opendir_common() into a new _filldir() function and call this from rewinddir() when operating on a unionfs directory.
  • If rewinddir() is called on a directory opened with fdopendir() before any directory entries are fetched, rewinddir() will not adjust the seek location of the backing file descriptor. If the file descriptor passed to fdopendir() had a non-zero offset, the rewinddir() will not rewind to the beginning. Fix this by always seeking back to 0 in rewinddir(). This means the dd_rewind hack can also be removed.
Test Plan

I wrote a test program that mixed calls of opendir() and fdopendir()
with rewinddir() and counted the number of entries found by readdir()
at each stage. For the fdopendir() the simple case was to open a
directory, use SEEK_END to seek to the end, then fdopendir/rewinddir.
The readdir() loop then found zero entries without the fix. For the
unionfs fixes, I setup a unionfs in a VM and ran the program under gdb
so that I could alter the state of the lower filesystem (add a new file
or remove a file) after a readdir() loop but before rewinddir() and
verify the program observed the updated count after the rewind.

Two further considerations:

  1. rewinddir() could probably call _reclaim_telldir() as POSIX says that positions returned from telldir() are invalidatd by a rewinddir().
  2. If fdopendir() is passed a non-directory and fails, the file is currently still marked as close-on-exec.

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
No Lint Coverage
Unit
No Test Coverage

Event Timeline

jhb retitled this revision from to Make rewinddir() always rewind..
jhb updated this object.
jhb edited the test plan for this revision. (Show Details)
jhb added a reviewer: jilles.
jilles requested changes to this revision.Jul 2 2014, 10:04 PM
jilles edited edge metadata.

During testing, I noticed several problems with the new fdopendir():

  • The entries returned by the initial getdirentries() call are not returned. Small directories appear empty while larger directories return a subset of the entries. This requires changes to readdir.c as well.
  • If the fd passed to fdopendir() does not refer to a directory, the [EINVAL] from _getdirentries() should be changed to the required [ENOTDIR] error.
This revision now requires changes to proceed.Jul 2 2014, 10:04 PM
jhb edited edge metadata.

Fix the two issues raised by jilles.

Do you have any thoughts on the the case of fdopendir() "leaking" the F_CLOEXEC flag if it fails with ENOTDIR?

jilles edited edge metadata.

The new version appears to work properly.

The close-on-exec flag can be left unchanged by fdopendir(), as glibc does and as allowed by POSIX. An application that cares about thread-related race conditions must open with O_CLOEXEC or similar anyway; a setting by fdopendir() would be too late.

This revision is now accepted and ready to land.Jul 10 2014, 10:21 PM