Commit f785a5ae authored by Nicholas Thomas's avatar Nicholas Thomas Committed by Kevin Wolf
Browse files

block/curl: Handle failed reads gracefully.



Current behaviour if a read fails is for the acb to not get finished.
This causes an infinite loop in bdrv_read_em (block.c). The read failure
never gets reported to the  guest and if the error condition clears, the
process never recovers.

With this patch, when curl reports a failure we finish the acb as a
failure. This results in the guest receiving an I/O error (rather than
the read hanging indefinitely) and if the error condition subsequently
clears, retries work as expected.

The simplest test is to put an ISO on a web server you have control over
and open it with qemu-io. Then move the ISO out of the way and attempt
to read some data - you should see behaviour matching the above.

Signed-off-by: default avatarNick Thomas <nick@bytemark.co.uk>
Signed-off-by: default avatarKevin Wolf <kwolf@redhat.com>
parent 3fba9d81
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -229,6 +229,23 @@ static void curl_multi_do(void *arg)
            {
                CURLState *state = NULL;
                curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);

                /* ACBs for successful messages get completed in curl_read_cb */
                if (msg->data.result != CURLE_OK) {
                    int i;
                    for (i = 0; i < CURL_NUM_ACB; i++) {
                        CURLAIOCB *acb = state->acb[i];

                        if (acb == NULL) {
                            continue;
                        }

                        acb->common.cb(acb->common.opaque, -EIO);
                        qemu_aio_release(acb);
                        state->acb[i] = NULL;
                    }
                }

                curl_clean_state(state);
                break;
            }
@@ -277,6 +294,7 @@ static CURLState *curl_init_state(BDRVCURLState *s)
    curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
    curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
    curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);

#ifdef DEBUG_VERBOSE
    curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);