commit 3ee1a1fc39819906f04d6c62c180e760cd3a689d upstream.
Add cifs_limit_kvec_subset() and select the appropriate limiter in cifs_send_async_read() to handle kvec iterators in async read path, fixing the EIO bug when running executables in cifs shares mounted with nolease.
This patch -- or equivalent patch, does not exist upstream, as the upstream code has suffered considerable API changes. The affected path is currently handled by netfs lib and located under netfs/direct_read.c.
Reproducer:
$ mount.cifs //server/share /mnt -o nolease $ cat - > /mnt/test.sh <<EOL echo hallo EOL $ chmod +x /mnt/test.sh $ /mnt/test.sh bash: /mnt/test.sh: /bin/bash: Defekter Interpreter: Eingabe-/Ausgabefehler $ rm -f /mnt/test.sh
Fixes: d08089f649a0 ("cifs: Change the I/O paths to use an iterator rather than a page list") Reported-by: Laura Kerner laura.kerner@ichaus.de Closes: https://bugzilla.suse.com/show_bug.cgi?id=1245449 Signed-off-by: Henrique Carvalho henrique.carvalho@suse.com --- fs/smb/client/file.c | 46 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-)
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index d883ed75022c..4878c74bae6f 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -3527,6 +3527,42 @@ static size_t cifs_limit_bvec_subset(const struct iov_iter *iter, size_t max_siz return span; }
+static size_t cifs_limit_kvec_subset(const struct iov_iter *iter, size_t max_size, + size_t max_segs, unsigned int *_nsegs) +{ + const struct kvec *kvecs = iter->kvec; + unsigned int nkv = iter->nr_segs, ix = 0, nsegs = 0; + size_t len, span = 0, n = iter->count; + size_t skip = iter->iov_offset; + + if (WARN_ON(!iov_iter_is_kvec(iter)) || n == 0) + return 0; + + while (n && ix < nkv && skip) { + len = kvecs[ix].iov_len; + if (skip < len) + break; + skip -= len; + n -= len; + ix++; + } + + while (n && ix < nkv) { + len = min3(n, kvecs[ix].iov_len - skip, max_size); + span += len; + max_size -= len; + nsegs++; + ix++; + if (max_size == 0 || nsegs >= max_segs) + break; + skip = 0; + n -= len; + } + + *_nsegs = nsegs; + return span; +} + static int cifs_write_from_iter(loff_t fpos, size_t len, struct iov_iter *from, struct cifsFileInfo *open_file, @@ -4079,6 +4115,13 @@ cifs_send_async_read(loff_t fpos, size_t len, struct cifsFileInfo *open_file, int rc; pid_t pid; struct TCP_Server_Info *server; + size_t (*limit_iov_subset)(const struct iov_iter *iter, size_t max_size, + size_t max_segs, unsigned int *_nsegs); + + if (iov_iter_is_kvec(&ctx->iter)) + limit_iov_subset = cifs_limit_kvec_subset; + else + limit_iov_subset = cifs_limit_bvec_subset;
server = cifs_pick_channel(tlink_tcon(open_file->tlink)->ses);
@@ -4113,8 +4156,7 @@ cifs_send_async_read(loff_t fpos, size_t len, struct cifsFileInfo *open_file,
max_len = min_t(size_t, len, rsize);
- cur_len = cifs_limit_bvec_subset(&ctx->iter, max_len, - max_segs, &nsegs); + cur_len = limit_iov_subset(&ctx->iter, max_len, max_segs, &nsegs); cifs_dbg(FYI, "read-to-iter len=%zx/%zx nsegs=%u/%lu/%u\n", cur_len, max_len, nsegs, ctx->iter.nr_segs, max_segs); if (cur_len == 0) {