I Hereby Propose to Rename EIO as EID'oh!
If you are implementing a Fuse filesystem it’s easier, ever more than usual, to commit to making silly mistakes over and over again. Case in point; obviously any dirent
structure shouldn’t have a name with a /
in it. However that simple fact might not be apparent if you forget the fact that a filesystem is supposed to be hiearchical. And in that case the error you get isn’t very clear (at first):
$ ls /tmp/.passfuse/aardvardk
ls: reading directory '/tmp/.passfuse/aardvardk': Input/output error
The above output is from where the victim culprit filesystem is mounted on /tmp/.passfuse
.
And according the strace the getdents64
is the system call getting the EIO
(output of strace -tf ls
):
388296 01:17:55 getdents64(3, 0x557842209460, 32768) = -1 EIO (Input/output error)
That should narrow it down; so after some 100 more tries you can finally try to grep for the definition of getdents64
in the Linux Kernel source. Which should lead to the fs/readdir.c
file and finally to this verification function:
/*
* POSIX says that a dirent name cannot contain NULL or a '/'.
* <snip>
*/
static int verify_dirent_name(const char *name, int len)
{
if (!len)
return -EIO;
if (memchr(name, '/', len))
return -EIO;
return 0;
}
Available in the latest stable Kernel source from:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/readdir.c?h=v5.4#n111
And carrying out the obvious fix of avoiding the logical mistake of putting slashes in the dirent
s undoes the mistake:
$ ls /tmp/.passfuse/aardvardk
foo.gpg
This has been a head-against-wall courtesy of passfuse