Package: rclone
Version: 1.60.1+dfsg-4
Severity: normal
X-Debbugs-CC: [email protected]
Dear maintainers,
rclone mount built from Debian sources answers FUSE xattr requests with
EIO instead of ENOSYS/EOPNOTSUPP, which makes tools that probe for
xattrs/ACLs (ls -l, getfattr, cp -a, rsync -X, tar --xattrs, ...) print
spurious "Input/output error" messages and return non-zero exit codes.
This is a packaging-combination bug between rclone and the very old
golang-bazil-fuse (2016 snapshot), not an upstream rclone bug.
# How to reproduce
rclone config create test webdav url=... vendor=other user=... pass=...
rclone mount test: /mnt/test &
touch /mnt/test/file
ls -l /mnt/test
Output:
ls: '/mnt/test/file': Input/output error
-rw-r--r-- 1 user user 0 ... file <- entry is still listed, stat is
fine
Direct syscall check:
$ python3 -c "import os; os.listxattr('/mnt/test/file')"
OSError: [Errno 5] Input/output error: '/mnt/test/file'
Directories behave correctly (EOPNOTSUPP), only regular files fail.
# Root cause analysis
rclone's FUSE handlers for regular files return a bare syscall.Errno
(cmd/mount/file.go, both 1.60.1+dfsg-4 and current sid 1.69.3+dfsg-3):
func (f *File) Listxattr(ctx context.Context, req *fuse.ListxattrRequest,
resp *fuse.ListxattrResponse) error {
return syscall.ENOSYS // we never implement this
}
(same for Getxattr/Setxattr/Removexattr; introduced by upstream commit
"mount: replace deprecated fuse.ENOSYS with syscall.ENOSYS", 2022-06-13,
first released in v1.60).
Upstream rclone pins bazil.org/fuse v0.0.0-20200524192727-fb710f7dfd05,
whose ToErrno() explicitly handles bare syscall.Errno:
func ToErrno(err error) Errno {
if err, ok := err.(syscall.Errno); ok {
return Errno(err)
}
...
However, the Debian build uses golang-bazil-fuse
0.0~git20160811.0.371fbbd-4 (2016-08-11), whose Header.RespondError only
recognizes types implementing the bazil ErrorNumber interface:
func (h *Header) RespondError(err error) {
errno := DefaultErrno // == EIO
if ferr, ok := err.(ErrorNumber); ok {
errno = ferr.Errno()
}
...
}
syscall.Errno does not implement bazil's ErrorNumber, so the assertion
fails and the kernel gets -EIO. (Directories work because the serve
layer returns fuse.ENOTSUP, which is a bazil Errno and thus implements
ErrorNumber.)
Confirmation with --debug-fuse:
fuse: <- Listxattr [ID=0x12 Node=0x2 ...] 256 @0
fuse: -> [ID=0x12] Listxattr error=EIO: function not implemented
Note the contradiction: errno says EIO while the underlying error string
is "function not implemented" (ENOSYS).
golang-bazil-fuse is at the same 2016 revision in trixie, forky and sid,
so upgrading rclone alone does not fix this; every rclone release since
v1.60 packaged in Debian is affected.
# Possible fixes
A) Minimal one-line-per-symbol patch to rclone: return fuse.ENOSYS
instead of syscall.ENOSYS in cmd/mount/file.go xattr handlers. The
fuse.ENOSYS constant exists in both the 2016 and current library
versions, so this is safe regardless of library version.
B) Alternatively, update golang-bazil-fuse to a newer upstream snapshot
(>= 2020-05-24, e.g. current master with FUSE protocol 7.33), whose
ToErrno handles bare syscall.Errno. This may need a rebuild of other
reverse dependencies.
I verified the analysis against both the trixie rclone 1.60.1+dfsg-4
(reproduced locally) and the sid source package 1.69.3+dfsg-3 (code
inspection via sources.debian.org).
No data corruption is involved: reads/writes/stat all work fine; the
impact is diagnostic noise and polluted exit codes for xattr-probing
tools.
Thanks for maintaining rclone in Debian!
--
choury