]> git.ipfire.org Git - people/ms/linux.git/commitdiff
fuse: ioctl: translate ENOSYS
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 21 Jul 2022 14:06:18 +0000 (16:06 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 21 Jul 2022 14:06:18 +0000 (16:06 +0200)
Overlayfs may fail to complete updates when a filesystem lacks
fileattr/xattr syscall support and responds with an ENOSYS error code,
resulting in an unexpected "Function not implemented" error.

This bug may occur with FUSE filesystems, such as davfs2.

Steps to reproduce:

  # install davfs2, e.g., apk add davfs2
  mkdir /test mkdir /test/lower /test/upper /test/work /test/mnt
  yes '' | mount -t davfs -o ro http://some-web-dav-server/path \
    /test/lower
  mount -t overlay -o upperdir=/test/upper,lowerdir=/test/lower \
    -o workdir=/test/work overlay /test/mnt

  # when "some-file" exists in the lowerdir, this fails with "Function
  # not implemented", with dmesg showing "overlayfs: failed to retrieve
  # lower fileattr (/some-file, err=-38)"
  touch /test/mnt/some-file

The underlying cause of this regresion is actually in FUSE, which fails to
translate the ENOSYS error code returned by userspace filesystem (which
means that the ioctl operation is not supported) to ENOTTY.

Reported-by: Christian Kohlschütter <christian@kohlschutter.com>
Fixes: 72db82115d2b ("ovl: copy up sync/noatime fileattr flags")
Fixes: 59efec7b9039 ("fuse: implement ioctl support")
Cc: <stable@vger.kernel.org>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/ioctl.c

index 33cde4bbccdc1b44196679065ef254efde8933f3..61d8afcb10a3fdd7d06cffc00b6c35626dd2577d 100644 (file)
@@ -9,6 +9,17 @@
 #include <linux/compat.h>
 #include <linux/fileattr.h>
 
+static ssize_t fuse_send_ioctl(struct fuse_mount *fm, struct fuse_args *args)
+{
+       ssize_t ret = fuse_simple_request(fm, args);
+
+       /* Translate ENOSYS, which shouldn't be returned from fs */
+       if (ret == -ENOSYS)
+               ret = -ENOTTY;
+
+       return ret;
+}
+
 /*
  * CUSE servers compiled on 32bit broke on 64bit kernels because the
  * ABI was defined to be 'struct iovec' which is different on 32bit
@@ -259,7 +270,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
        ap.args.out_pages = true;
        ap.args.out_argvar = true;
 
-       transferred = fuse_simple_request(fm, &ap.args);
+       transferred = fuse_send_ioctl(fm, &ap.args);
        err = transferred;
        if (transferred < 0)
                goto out;
@@ -393,7 +404,7 @@ static int fuse_priv_ioctl(struct inode *inode, struct fuse_file *ff,
        args.out_args[1].size = inarg.out_size;
        args.out_args[1].value = ptr;
 
-       err = fuse_simple_request(fm, &args);
+       err = fuse_send_ioctl(fm, &args);
        if (!err) {
                if (outarg.result < 0)
                        err = outarg.result;