fs: check FMODE_LSEEK to control internal pipe splicing
authorJason A. Donenfeld <Jason@zx2c4.com>
Wed, 29 Jun 2022 13:06:58 +0000 (15:06 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Aug 2022 09:11:18 +0000 (11:11 +0200)
[ Upstream commit 97ef77c52b789ec1411d360ed99dca1efe4b2c81 ]

The original direct splicing mechanism from Jens required the input to
be a regular file because it was avoiding the special socket case. It
also recognized blkdevs as being close enough to a regular file. But it
forgot about chardevs, which behave the same way and work fine here.

This is an okayish heuristic, but it doesn't totally work. For example,
a few chardevs should be spliceable here. And a few regular files
shouldn't. This patch fixes this by instead checking whether FMODE_LSEEK
is set, which represents decently enough what we need rewinding for when
splicing to internal pipes.

Fixes: b92ce5589374 ("[PATCH] splice: add direct fd <-> fd splicing support")
Cc: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/splice.c

index c84ac7e97e215b4eb0985edebc8d2c87c3a8e0a7..04d25af25a42cc043c4d860f2d7ec8984bb92072 100644 (file)
@@ -898,17 +898,15 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
 {
        struct pipe_inode_info *pipe;
        long ret, bytes;
-       umode_t i_mode;
        size_t len;
        int i, flags, more;
 
        /*
-        * We require the input being a regular file, as we don't want to
-        * randomly drop data for eg socket -> socket splicing. Use the
-        * piped splicing for that!
+        * We require the input to be seekable, as we don't want to randomly
+        * drop data for eg socket -> socket splicing. Use the piped splicing
+        * for that!
         */
-       i_mode = file_inode(in)->i_mode;
-       if (unlikely(!S_ISREG(i_mode) && !S_ISBLK(i_mode)))
+       if (unlikely(!(in->f_mode & FMODE_LSEEK)))
                return -EINVAL;
 
        /*