Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix readdir() bug when a non-zero offset is specified in filler #269

Merged
merged 2 commits into from Jul 21, 2018

Conversation

rostislav
Copy link
Contributor

Hello,

The first commit adds a test of seekdir(), and the second fixes the bug that is exposed by the test.
I am not sure this is the best fix, but I tried to change as little as possible.

Another thing is that 2.9.x branch is also affected by the same bug. Would a similar fix be accepted there as well?

@Nikratio
Copy link
Contributor

Thanks for tracking this down! I think your fix is fine. I have just one request: could you please also add a comment to ChangeLog.rst?

Rostislav Skudnov added 2 commits July 20, 2018 20:25
Test that after reading the whole directory with readdir(), the offsets
returned by telldir() are still valid.
The bug occurs when a filesystem client reads a directory until the end,
seeks using seekdir() to some valid non-zero position and calls
readdir(). A valid 'struct dirent *' is expected, but NULL is returned
instead. Pseudocode demonstrating the bug:

DIR *dp = opendir("some_dir");
struct dirent *de = readdir(dp);

/* Get offset of the second entry */
long offset = telldir(dp);

/* Read directory until the end */
while (de)
	de = readdir(de);

seekdir(dp, offset);
de = readdir(dp);
/* de must contain the second entry, but NULL is returned instead */

The reason of the bug is that when the end of directory is reached, the
kernel calls FUSE_READDIR op with an offset at the end of directory, so
the filesystem's .readdir callback never calls the filler function, and
we end up with dh->filled set to 1. After seekdir(), FUSE_READDIR is
called again with a new offset, but this time the filesystem's .readdir
callback is never called, and an empty reply is returned.

Fix by setting dh->filled to 1 only when zero offsets are given to
filler function.
@rostislav
Copy link
Contributor Author

Thanks for review! I've added comments to ChangeLog.rst and edited commit messages a bit.

@Nikratio Nikratio merged commit 5f125c5 into libfuse:master Jul 21, 2018
@rostislav rostislav deleted the fix_readdir branch July 21, 2018 21:07
rostislav added a commit to rostislav/libfuse that referenced this pull request Jul 24, 2018
The bug occurs when a filesystem client reads a directory until the end,
seeks using seekdir() to some valid non-zero position and calls
readdir(). A valid 'struct dirent *' is expected, but NULL is returned
instead. Pseudocode demonstrating the bug:

DIR *dp = opendir("some_dir");
struct dirent *de = readdir(dp);

/* Get offset of the second entry */
long offset = telldir(dp);

/* Read directory until the end */
while (de)
	de = readdir(de);

seekdir(dp, offset);
de = readdir(dp);
/* de must contain the second entry, but NULL is returned instead */

The reason of the bug is that when the end of directory is reached, the
kernel calls FUSE_READDIR op with an offset at the end of directory, so
the filesystem's .readdir callback never calls the filler function, and
we end up with dh->filled set to 1. After seekdir(), FUSE_READDIR is
called again with a new offset, but this time the filesystem's .readdir
callback is never called, and an empty reply is returned.

Fix by setting dh->filled to 1 only when zero offsets are given to
filler function.

This commit is backported from the following commit in 'master' branch:

commit 5f125c5 (myfork/master)
Author: Rostislav <rostislav@users.noreply.github.com>
Date:   Sat Jul 21 12:57:09 2018 +0300

    Fix readdir() bug when a non-zero offset is specified in filler (libfuse#269)
rostislav pushed a commit to rostislav/libfuse that referenced this pull request Jul 25, 2018
The bug occurs when a filesystem client reads a directory until the end,
seeks using seekdir() to some valid non-zero position and calls
readdir(). A valid 'struct dirent *' is expected, but NULL is returned
instead. Pseudocode demonstrating the bug:

DIR *dp = opendir("some_dir");
struct dirent *de = readdir(dp);

/* Get offset of the second entry */
long offset = telldir(dp);

/* Read directory until the end */
while (de)
	de = readdir(de);

seekdir(dp, offset);
de = readdir(dp);
/* de must contain the second entry, but NULL is returned instead */

The reason of the bug is that when the end of directory is reached, the
kernel calls FUSE_READDIR op with an offset at the end of directory, so
the filesystem's .readdir callback never calls the filler function, and
we end up with dh->filled set to 1. After seekdir(), FUSE_READDIR is
called again with a new offset, but this time the filesystem's .readdir
callback is never called, and an empty reply is returned.

Fix by setting dh->filled to 1 only when zero offsets are given to
filler function.

This commit is backported from the following commit in 'master' branch:

commit 5f125c5
Author: Rostislav <rostislav@users.noreply.github.com>
Date:   Sat Jul 21 12:57:09 2018 +0300

    Fix readdir() bug when a non-zero offset is specified in filler (libfuse#269)
rostislav pushed a commit to rostislav/libfuse that referenced this pull request Jul 25, 2018
The bug occurs when a filesystem client reads a directory until the end,
seeks using seekdir() to some valid non-zero position and calls
readdir(). A valid 'struct dirent *' is expected, but NULL is returned
instead. Pseudocode demonstrating the bug:

DIR *dp = opendir("some_dir");
struct dirent *de = readdir(dp);

/* Get offset of the second entry */
long offset = telldir(dp);

/* Read directory until the end */
while (de)
	de = readdir(de);

seekdir(dp, offset);
de = readdir(dp);
/* de must contain the second entry, but NULL is returned instead */

The reason of the bug is that when the end of directory is reached, the
kernel calls FUSE_READDIR op with an offset at the end of directory, so
the filesystem's .readdir callback never calls the filler function, and
we end up with dh->filled set to 1. After seekdir(), FUSE_READDIR is
called again with a new offset, but this time the filesystem's .readdir
callback is never called, and an empty reply is returned.

Fix by setting dh->filled to 1 only when zero offsets are given to
filler function.

This commit is backported from the following commit in 'master' branch:

commit 5f125c5
Author: Rostislav <rostislav@users.noreply.github.com>
Date:   Sat Jul 21 12:57:09 2018 +0300

    Fix readdir() bug when a non-zero offset is specified in filler (libfuse#269)
Nikratio pushed a commit that referenced this pull request Jul 25, 2018
The bug occurs when a filesystem client reads a directory until the end,
seeks using seekdir() to some valid non-zero position and calls
readdir(). A valid 'struct dirent *' is expected, but NULL is returned
instead. Pseudocode demonstrating the bug:

DIR *dp = opendir("some_dir");
struct dirent *de = readdir(dp);

/* Get offset of the second entry */
long offset = telldir(dp);

/* Read directory until the end */
while (de)
	de = readdir(de);

seekdir(dp, offset);
de = readdir(dp);
/* de must contain the second entry, but NULL is returned instead */

The reason of the bug is that when the end of directory is reached, the
kernel calls FUSE_READDIR op with an offset at the end of directory, so
the filesystem's .readdir callback never calls the filler function, and
we end up with dh->filled set to 1. After seekdir(), FUSE_READDIR is
called again with a new offset, but this time the filesystem's .readdir
callback is never called, and an empty reply is returned.

Fix by setting dh->filled to 1 only when zero offsets are given to
filler function.

This commit is backported from the following commit in 'master' branch:

commit 5f125c5
Author: Rostislav <rostislav@users.noreply.github.com>
Date:   Sat Jul 21 12:57:09 2018 +0300

    Fix readdir() bug when a non-zero offset is specified in filler (#269)
@szmi szmi mentioned this pull request Oct 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants