plugins: Don't lose original error when emulating FUA
commit95a72f5d913a281f07c1fc83596338d53fd24877
authorEric Blake <eblake@redhat.com>
Tue, 13 Aug 2019 02:04:19 +0000 (12 21:04 -0500)
committerEric Blake <eblake@redhat.com>
Tue, 13 Aug 2019 12:53:49 +0000 (13 07:53 -0500)
treece6173521c5b9a8e143447b1c0c2a8f71fe0fd3f
parent9330e96a4a14902640d3b55c9cf2c51f9ad8e6c5
plugins: Don't lose original error when emulating FUA

Calling plugin_flush(b, conn, 0, err) instead of the longer
p->plugin.flush(connection_get_handle(conn, 0) has an unfortunate side
effect: if .can_fua is emulate, and the client requested FUA, there is
a possibility that a failed attempt to flush has set neither the
threadlocal error nor errno to a sane value (the most obvious case is
when the plugin lacks .flush, and directly sets *err to EINVAL).
However, when the caller detects that flush failed, it blindly assigns
*err = get_error(p), which will default to EIO if nothing else can be
located in threadlocal or errno.

In practice, this corner case requires a custom plugin to hit: the sh
plugin seemed like the most obvious candidate, but it provides a
.flush callback that reliably sets errno on failure (so the subsequent
call to get_error(p) happens to reset *err to the value it already
had).  Other bindings like Python haven't even been converted to
version 2 API yet, which means .can_fua can only be set indirectly
based on .flush existing.

But it's easy enough to prevent: after calling any other plugin_*
function fails, only change *err if it is not already set; in turn
this means we don't have to mess with errno after plugin_pwrite fails
during .zero fallback.

Fixes: 20db811e
Signed-off-by: Eric Blake <eblake@redhat.com>
server/plugins.c