static coroutine_fn void nbd_trip(void *opaque)

{

    NBDClient *client = opaque;

    NBDExport *exp = client->exp;

    NBDRequestData *req;

    NBDRequest request = { 0 };    /* GCC thinks it can be used uninitialized */

    int ret;

    int flags;

    int reply_data_len = 0;

    Error *local_err = NULL;

    char *msg = NULL;



    trace_nbd_trip();

    if (client->closing) {

        nbd_client_put(client);

        return;

    }



    req = nbd_request_get(client);

    ret = nbd_co_receive_request(req, &request, &local_err);

    client->recv_coroutine = NULL;

    nbd_client_receive_next_request(client);

    if (ret == -EIO) {

        goto disconnect;

    }



    if (ret < 0) {

        goto reply;

    }



    if (client->closing) {

        /*

         * The client may be closed when we are blocked in

         * nbd_co_receive_request()

         */

        goto done;

    }



    switch (request.type) {

    case NBD_CMD_READ:

        /* XXX: NBD Protocol only documents use of FUA with WRITE */

        if (request.flags & NBD_CMD_FLAG_FUA) {

            ret = blk_co_flush(exp->blk);

            if (ret < 0) {

                error_setg_errno(&local_err, -ret, "flush failed");

                break;

            }

        }



        ret = blk_pread(exp->blk, request.from + exp->dev_offset,

                        req->data, request.len);

        if (ret < 0) {

            error_setg_errno(&local_err, -ret, "reading from file failed");

            break;

        }



        reply_data_len = request.len;



        break;

    case NBD_CMD_WRITE:

        if (exp->nbdflags & NBD_FLAG_READ_ONLY) {

            error_setg(&local_err, "Export is read-only");

            ret = -EROFS;

            break;

        }



        flags = 0;

        if (request.flags & NBD_CMD_FLAG_FUA) {

            flags |= BDRV_REQ_FUA;

        }

        ret = blk_pwrite(exp->blk, request.from + exp->dev_offset,

                         req->data, request.len, flags);

        if (ret < 0) {

            error_setg_errno(&local_err, -ret, "writing to file failed");

        }



        break;

    case NBD_CMD_WRITE_ZEROES:

        if (exp->nbdflags & NBD_FLAG_READ_ONLY) {

            error_setg(&local_err, "Export is read-only");

            ret = -EROFS;

            break;

        }



        flags = 0;

        if (request.flags & NBD_CMD_FLAG_FUA) {

            flags |= BDRV_REQ_FUA;

        }

        if (!(request.flags & NBD_CMD_FLAG_NO_HOLE)) {

            flags |= BDRV_REQ_MAY_UNMAP;

        }

        ret = blk_pwrite_zeroes(exp->blk, request.from + exp->dev_offset,

                                request.len, flags);

        if (ret < 0) {

            error_setg_errno(&local_err, -ret, "writing to file failed");

        }



        break;

    case NBD_CMD_DISC:

        /* unreachable, thanks to special case in nbd_co_receive_request() */

        abort();



    case NBD_CMD_FLUSH:

        ret = blk_co_flush(exp->blk);

        if (ret < 0) {

            error_setg_errno(&local_err, -ret, "flush failed");

        }



        break;

    case NBD_CMD_TRIM:

        ret = blk_co_pdiscard(exp->blk, request.from + exp->dev_offset,

                              request.len);

        if (ret < 0) {

            error_setg_errno(&local_err, -ret, "discard failed");

        }



        break;

    default:

        error_setg(&local_err, "invalid request type (%" PRIu32 ") received",

                   request.type);

        ret = -EINVAL;

    }



reply:

    if (local_err) {

        /* If we get here, local_err was not a fatal error, and should be sent

         * to the client. */

        assert(ret < 0);

        msg = g_strdup(error_get_pretty(local_err));

        error_report_err(local_err);

        local_err = NULL;

    }



    if (client->structured_reply &&

        (ret < 0 || request.type == NBD_CMD_READ)) {

        if (ret < 0) {

            ret = nbd_co_send_structured_error(req->client, request.handle,

                                               -ret, msg, &local_err);

        } else {

            ret = nbd_co_send_structured_read(req->client, request.handle,

                                              request.from, req->data,

                                              reply_data_len, &local_err);

        }

    } else {

        ret = nbd_co_send_simple_reply(req->client, request.handle,

                                       ret < 0 ? -ret : 0,

                                       req->data, reply_data_len, &local_err);

    }

    g_free(msg);

    if (ret < 0) {

        error_prepend(&local_err, "Failed to send reply: ");

        goto disconnect;

    }



    /* We must disconnect after NBD_CMD_WRITE if we did not

     * read the payload.

     */

    if (!req->complete) {

        error_setg(&local_err, "Request handling failed in intermediate state");

        goto disconnect;

    }



done:

    nbd_request_put(req);

    nbd_client_put(client);

    return;



disconnect:

    if (local_err) {

        error_reportf_err(local_err, "Disconnect client, due to: ");

    }

    nbd_request_put(req);

    client_close(client, true);

    nbd_client_put(client);

}
