static void nbd_trip(void *opaque) { NBDClient *client = opaque; NBDExport *exp = client->exp; NBDRequest *req; struct nbd_request request; struct nbd_reply reply; ssize_t ret; uint32_t command; TRACE("Reading request."); if (client->closing) { return; } req = nbd_request_get(client); ret = nbd_co_receive_request(req, &request); if (ret == -EAGAIN) { goto done; } if (ret == -EIO) { goto out; } reply.handle = request.handle; reply.error = 0; if (ret < 0) { reply.error = -ret; goto error_reply; } command = request.type & NBD_CMD_MASK_COMMAND; if (command != NBD_CMD_DISC && (request.from + request.len) > exp->size) { LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 ", Offset: %" PRIu64 "\n", request.from, request.len, (uint64_t)exp->size, (uint64_t)exp->dev_offset); LOG("requested operation past EOF--bad client?"); goto invalid_request; } if (client->closing) { /* * The client may be closed when we are blocked in * nbd_co_receive_request() */ goto done; } switch (command) { case NBD_CMD_READ: TRACE("Request type is READ"); if (request.type & NBD_CMD_FLAG_FUA) { ret = blk_co_flush(exp->blk); if (ret < 0) { LOG("flush failed"); reply.error = -ret; goto error_reply; } } ret = blk_pread(exp->blk, request.from + exp->dev_offset, req->data, request.len); if (ret < 0) { LOG("reading from file failed"); reply.error = -ret; goto error_reply; } TRACE("Read %u byte(s)", request.len); if (nbd_co_send_reply(req, &reply, request.len) < 0) goto out; break; case NBD_CMD_WRITE: TRACE("Request type is WRITE"); if (exp->nbdflags & NBD_FLAG_READ_ONLY) { TRACE("Server is read-only, return error"); reply.error = EROFS; goto error_reply; } TRACE("Writing to device"); ret = blk_pwrite(exp->blk, request.from + exp->dev_offset, req->data, request.len, 0); if (ret < 0) { LOG("writing to file failed"); reply.error = -ret; goto error_reply; } if (request.type & NBD_CMD_FLAG_FUA) { ret = blk_co_flush(exp->blk); if (ret < 0) { LOG("flush failed"); reply.error = -ret; goto error_reply; } } if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; } break; case NBD_CMD_DISC: TRACE("Request type is DISCONNECT"); errno = 0; goto out; case NBD_CMD_FLUSH: TRACE("Request type is FLUSH"); ret = blk_co_flush(exp->blk); if (ret < 0) { LOG("flush failed"); reply.error = -ret; } if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; } break; case NBD_CMD_TRIM: TRACE("Request type is TRIM"); ret = blk_co_discard(exp->blk, (request.from + exp->dev_offset) / BDRV_SECTOR_SIZE, request.len / BDRV_SECTOR_SIZE); if (ret < 0) { LOG("discard failed"); reply.error = -ret; } if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; } break; default: LOG("invalid request type (%u) received", request.type); invalid_request: reply.error = EINVAL; error_reply: if (nbd_co_send_reply(req, &reply, 0) < 0) { goto out; } break; } TRACE("Request/Reply complete"); done: nbd_request_put(req); return; out: nbd_request_put(req); client_close(client); }