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

Improper use of offset in readdir #63

Open
vasi opened this issue Aug 8, 2024 · 4 comments
Open

Improper use of offset in readdir #63

vasi opened this issue Aug 8, 2024 · 4 comments

Comments

@vasi
Copy link

vasi commented Aug 8, 2024

Expected behaviour

FUSE-T should call readdir with an offset value returned from a previous call to readdir. For example, the following sequence of calls and return values:

> readdir(ino = 1, off = 0)
< fuse_reply_buf(size = 60), containing:
<    direntry(name = "foo", off = 3000)
<    direntry(name = "bar", off = 5000)
<    direntry(name = "Iggy", off = 2000)
> readdir(ino = 1, off = 2000)
< fuse_reply buf
<    direntry(name = "Iggy", off = 2000)
...

Actual behaviour

FUSE-T calls readdir with the size of the buf from a previous call:

> readdir(ino = 1, off = 0)
< fuse_reply_buf(size = 60), containing:
<    direntry(name = "foo", off = 3000)
<    direntry(name = "bar", off = 5000)
<    direntry(name = "Iggy", off = 2000)
> readdir(ino = 1, off = 60)
????

Explanation

The FUSE readdir op is documented to take an offset parameter, whose value must be an offset previously returned in a direntry by an earlier call to readdir. That means the filesystem can simply provide an arbitrary offset value, and use it later—it doesn't have to correspond to a real byte-offset in memory or the FUSE protocol or anywhere else. For example, look at this comment from the sapling filesystem, which talks about returning a hash as the offset.

FUSE-T's out-of-spec behaviour is semi-documented in the FUSE-T wiki, which says:

READDIR call should return all results in the first pass.

However, this can't be guaranteed by the filesystem. The caller of readdir sets a maximum size, which a sufficiently large directory may exceed.

Impact

When I attempt to use my filesystem, squashfuse with FUSE-T, listing directories yields an error. This is because squashfuse has never seen the offset that FUSE-T provides on the second call to readdir, and so is unable to figure out where it should start listing.

Workarounds

It's possible for filesystems to respect FUSE-T's out-of-spec offsets. For example, see this PR for squashfuse . However, this requires either using sub-optimal code for other FUSE implementations, or making whoever is building the filesystem select which behaviour to use.

If FUSE-T can't fix readdir quickly, it would at least be nice to provide some way for filesystems to automatically detect this improper use of offset, either at runtime or build-time.

@macos-fuse-t
Copy link
Owner

As far as I remember there was a reason for such behavior related to NFS macOS client implementation. If I'm not mistaken macOS expects all readdir entries to be returned in a single pass. Your argument is valid however. I think it should be possible to do a workaround by calling FUSE READDIR multiple times. I'll try to correct this.

@macos-fuse-t
Copy link
Owner

Dave,
Try this fuse-t pre-release. Tell me if that fixes the problem.
https://github.com/macos-fuse-t/fuse-t/releases/tag/1.0.39e

@vasi
Copy link
Author

vasi commented Aug 11, 2024

Looks good! Thank you for fixing this

@sysaulab
Copy link

I am writing my first FS running FUSE-T 1.0.41 and MacOS 14.6.1, I have a similar problem.

I want to expose 65536 files to my root folder but when It requires 64 readdir requests to complete. Using a log I know I pass and receive my valid offset of the work done matches the expectation.

The problem is the right after the directory is released it is hit with another open request followed by 64 readdir calls then a close then an open and the loop never ends. I need to force kill my program otherwise I may end up with a corrupted Finder. It even crashed my Mac...

I tried to see in the documentation and examples if I missed a call or signal that tells its over but could not figure it out.

This is my filesystem's code

It creates enormous files with static random data that can be address directly. I already filed a bug report with apple for letting me using 64 bit offsets but limit effective file size description to 63 bits. Fully deployed one generator create 1 yobibyte of fully addressable random bytes.

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

No branches or pull requests

3 participants