Skip to content

fix: replace assert(offset==0) with early return in readdir#371

Open
Yike-Ye wants to merge 1 commit into
libfuse:masterfrom
Yike-Ye:master
Open

fix: replace assert(offset==0) with early return in readdir#371
Yike-Ye wants to merge 1 commit into
libfuse:masterfrom
Yike-Ye:master

Conversation

@Yike-Ye

@Yike-Ye Yike-Ye commented Jun 29, 2026

Copy link
Copy Markdown

On macOS with macFUSE 5.3.x (FSKit backend) and macOS 27 Golden Gate beta, Finder/Spotlight may call readdir with a non-zero offset even when fuse_fill_dir is always called with offset=0 (old-style readdir). This triggers the assert and aborts the sshfs process.

Replace assert(offset == 0) with if (offset != 0) return 0 in:

  • sftp_readdir_async() in sshfs.c
  • sftp_readdir_sync() in sshfs.c
  • cache_readdir() in cache.c

Since all entries are returned in the first (offset=0) call, returning 0 on subsequent calls is semantically correct and prevents the crash.

Fixes: #338
Tested: macOS 27 Golden Gate beta 2, macFUSE 5.3.2

Comment thread cache.c

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why assert that offset must be 0, and then handle the case where it isn't?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because previously sshfs uses buf_get_entries (sshfs.c: 985) with offset=0, and assert(offset=0) is then used in sftp_readdir_async / sftp_readdir_sync for bug detection. In this process, all directory entries are returned from the server within one step. If the offset is non-zero, something might be wrong. See the notes in fuse.h:

	/** Read directory
	 *
	 * The filesystem may choose between two modes of operation:
	 *
	 * 1) The readdir implementation ignores the offset parameter, and
	 * passes zero to the filler function's offset.  The filler
	 * function will not return '1' (unless an error happens), so the
	 * whole directory is read in a single readdir operation.
	 *
	 * 2) The readdir implementation keeps track of the offsets of the
	 * directory entries.  It uses the offset parameter and always
	 * passes non-zero offset to the filler function.  When the buffer
	 * is full (or an error happens) the filler function will return
	 * '1'.
	 *
	 * When FUSE_READDIR_PLUS is not set, only some parameters of the
	 * fill function (the fuse_fill_dir_t parameter) are actually used:
	 * The file type (which is part of stat::st_mode) is used. And if
	 * fuse_config::use_ino is set, the inode (stat::st_ino) is also
	 * used. The other fields are ignored when FUSE_READDIR_PLUS is not
	 * set.
	 */
	FUSE_DARWIN_EXTEND_OPERATION(
		readdir,
		int (*) (const char *, void *, fuse_fill_dir_t, off_t,
			 struct fuse_file_info *, enum fuse_readdir_flags),
		int (*) (const char *, void *, fuse_darwin_fill_dir_t, off_t,
			 struct fuse_file_info *, enum fuse_readdir_flags)
	)

However, the updated MacFuse uses the new FSKit backend instead of kernel extension backend. This will sometimes deliver a non-zero offset, and thus lead to abort and disconnection.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But won't the assert cause the code to bail on a non-zero offset? Do you perhaps want to #ifdef the assert so it isn't active on macOS?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have replaced the assert(offset == 0) with if (offset !=0) return 0, which will return early instead of aborting the sshfs process. Although this may affect the FSKit backend behavior I mentioned, it works well for me so far. By the way, the #ifdef flag suggestion is a good point. I will update the PR shortly.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I got what you meant in the beginning. There was a typo and have been fixed in the update =)

On macOS with macFUSE 5.3.x (FSKit backend) and macOS 27 Golden Gate
beta, Finder/Spotlight may call readdir with a non-zero offset even
when fuse_fill_dir is always called with offset=0 (old-style readdir).
This triggers the assert and aborts the sshfs process.

Replace assert(offset == 0) with if (offset != 0) return 0 in:
- sftp_readdir_async() in sshfs.c
- sftp_readdir_sync() in sshfs.c
- cache_readdir() in cache.c

Since all entries are returned in the first (offset=0) call, returning
0 on subsequent calls is semantically correct and prevents the crash.

Fixes: libfuse#338
Tested: macOS 27 Golden Gate beta 2, macFUSE 5.3.2
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.

On macOS, anything involving the Finder crashes sshfs

2 participants