diff --git a/modules/network/lwnbdsvr/Makefile b/modules/network/lwnbdsvr/Makefile index 0f4a2998b..de31db2a4 100644 --- a/modules/network/lwnbdsvr/Makefile +++ b/modules/network/lwnbdsvr/Makefile @@ -1,5 +1,5 @@ IOP_BIN = lwnbdsvr.irx -IOP_OBJS = lwnbdsvr.o imports.o exports.o drivers/atad.o lwNBD/nbd_server.o +IOP_OBJS = lwnbdsvr.o imports.o exports.o drivers/atad.o lwNBD/nbd_server.o lwNBD/nbd_protocol.o IOP_CFLAGS += -DPS2SDK IOP_INCS += -I../../iopcore/common -I../../../include/ diff --git a/modules/network/lwnbdsvr/drivers/atad.c b/modules/network/lwnbdsvr/drivers/atad.c index c95cde23e..6fab337b8 100644 --- a/modules/network/lwnbdsvr/drivers/atad.c +++ b/modules/network/lwnbdsvr/drivers/atad.c @@ -1,6 +1,6 @@ #include "atad.h" -int hdd_atad_init(struct nbd_context *me) +int atad_init(struct nbd_context *me) { ata_devinfo_t *dev_info = ata_get_devinfo(0); if (dev_info != NULL && dev_info->exists) { @@ -10,17 +10,17 @@ int hdd_atad_init(struct nbd_context *me) return 1; } -int hdd_atad_read(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length) +int atad_read(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length) { return ata_device_sector_io(0, buffer, (uint32_t)offset, length, ATA_DIR_READ); } -int hdd_atad_write(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length) +int atad_write(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length) { return ata_device_sector_io(0, buffer, (uint32_t)offset, length, ATA_DIR_WRITE); } -int hdd_atad_flush(struct nbd_context *me) +int atad_flush(struct nbd_context *me) { return ata_device_flush_cache(0); } diff --git a/modules/network/lwnbdsvr/drivers/atad.h b/modules/network/lwnbdsvr/drivers/atad.h index e52fb4440..973bcc852 100644 --- a/modules/network/lwnbdsvr/drivers/atad.h +++ b/modules/network/lwnbdsvr/drivers/atad.h @@ -1,5 +1,3 @@ - - #ifndef ATAD_DRIVERS_NBD_H #define ATAD_DRIVERS_NBD_H @@ -10,10 +8,10 @@ extern "C" { #endif -int hdd_atad_init(struct nbd_context *me); -int hdd_atad_read(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length); -int hdd_atad_write(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length); -int hdd_atad_flush(struct nbd_context *me); +int atad_init(struct nbd_context *me); +int atad_read(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length); +int atad_write(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length); +int atad_flush(struct nbd_context *me); #ifdef __cplusplus } diff --git a/modules/network/lwnbdsvr/drivers/drivers.h b/modules/network/lwnbdsvr/drivers/drivers.h index 0da6c8804..c92ae31e0 100644 --- a/modules/network/lwnbdsvr/drivers/drivers.h +++ b/modules/network/lwnbdsvr/drivers/drivers.h @@ -1,5 +1,3 @@ - - #ifndef DRIVERS_NBD_H #define DRIVERS_NBD_H @@ -13,18 +11,19 @@ extern "C" { static struct nbd_context hdd_atad = { - .export_name = "hdd", .export_desc = "PlayStation 2 HDD via ATAD", + .export_name = "hdd0", .blockshift = 9, .buffer = nbd_buffer, - .export_init = hdd_atad_init, - .read = hdd_atad_read, - .write = hdd_atad_write, - .flush = hdd_atad_flush, + .export_init = atad_init, + .read = atad_read, + .write = atad_write, + .flush = atad_flush, }; struct nbd_context *nbd_contexts[] = { &hdd_atad, + NULL, }; #ifdef __cplusplus diff --git a/modules/network/lwnbdsvr/lwNBD/README.md b/modules/network/lwnbdsvr/lwNBD/README.md index c883271f6..2d74ddfdc 100644 --- a/modules/network/lwnbdsvr/lwNBD/README.md +++ b/modules/network/lwnbdsvr/lwNBD/README.md @@ -1,21 +1,28 @@ -#lwNBD +# lwNBD -* Description : A Lightweight NBD server based on lwIP stack -* Official repository : https://github.com/bignaux/lwNBD -* Author : Ronan Bignaux -* Licence : BSD -* lwIP 2.0.0 Socket API +* Description : A Lightweight NBD server based on lwIP stack +* Official repository : +* Author : Ronan Bignaux +* Licence : BSD +* lwIP 2.0.0 Socket API -#History +## History -On Playstation 2, there is no standardised central partition table like GPT for hard disk partitioning, nor is there a standard file system but PFS and HDLoader. In fact, there are few tools capable of handling hard disks, especially under Linux, and the servers developed in the past to handle these disks via the network did not use a standard protocol, which required each software wishing to handle the disks to include a specific client part, which were broken when the toolchain was updated. The same goes for the memory cards and other block devices on this console, which is why I decided to implement NBD on this target first. +On Playstation 2, there is no standardized central partition table like GPT for hard disk partitioning, nor is there a standard file system but PFS and HDLoader. In fact, there are few tools capable of handling hard disks, especially under Linux, and the servers developed in the past to handle these disks via the network did not use a standard protocol, which required each software wishing to handle the disks to include a specific client part, which were broken when the toolchain was updated. The same goes for the memory cards and other block devices on this console, which is why I decided to implement NBD on this target first. -lwNBD is developed on MIPS R3000 IOP in [my OPL nbd branch](https://github.com/bignaux/Open-PS2-Loader/tree/nbd/modules/network/lwnbdsvr), that provides a good uptodate use case. +lwNBD is developed on MIPS R3000 IOP in [my OPL nbd branch](https://github.com/bignaux/Open-PS2-Loader/tree/nbd/modules/network/lwnbdsvr), that provides a good uptodate use case. Currently, the support for HDD is merged in OPL. -#Status +## Status -Although this server is not yet complete in respect of the minimal requirements defined by the [NBD protocol](https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#baseline), it is nevertheless usable with certain clients, in particular nbdfuse (provided by libnbd), the client I am using for this development. In a [RERO spirit](https://en.wikipedia.org/wiki/Release_early,_release_often) i publish this "AS-IS". +Although this server is not yet complete in respect of the minimal requirements defined by the [NBD protocol](https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#baseline), it is nevertheless usable with certain clients. In a [RERO spirit](https://en.wikipedia.org/wiki/Release_early,_release_often) i publish this "AS-IS". -#TODO: -* fix NBD_FLAG_FIXED_NEWSTYLE negotiation -* NBD_OPT_INFO/NBD_OPT_GO, the server is not yet able to serve many export. Currently, the server takes the first on the list. +Known supported clients : + +* nbdfuse (provided by libnbd), works on windows with WSL2. +* nbd-client -no-optgo + +## TODO + +* A GNU/Linux port +* fix NBD_FLAG_FIXED_NEWSTYLE negotiation +* NBD_OPT_INFO/NBD_OPT_GO, the server is not yet able to serve many export. Currently, the server takes the first on the list. diff --git a/modules/network/lwnbdsvr/lwNBD/nbd_opts.h b/modules/network/lwnbdsvr/lwNBD/nbd_opts.h index cd7e32050..a574b950e 100644 --- a/modules/network/lwnbdsvr/lwNBD/nbd_opts.h +++ b/modules/network/lwnbdsvr/lwNBD/nbd_opts.h @@ -1,15 +1,15 @@ /****************************************************************/ /** - * - * @file nbd_opts.h - * - * @author Ronan Bignaux - * - * @brief Network Block Device Protocol implementation options - * - * Copyright (c) Ronan Bignaux. 2021 - * All rights reserved. - * - ********************************************************************/ + * + * @file nbd_opts.h + * + * @author Ronan Bignaux + * + * @brief Network Block Device Protocol implementation options + * + * Copyright (c) Ronan Bignaux. 2021 + * All rights reserved. + * + ********************************************************************/ /* * Redistribution and use in source and binary forms, with or without diff --git a/modules/network/lwnbdsvr/lwNBD/nbd_protocol.c b/modules/network/lwnbdsvr/lwNBD/nbd_protocol.c new file mode 100644 index 000000000..1b6ea32c1 --- /dev/null +++ b/modules/network/lwnbdsvr/lwNBD/nbd_protocol.c @@ -0,0 +1,345 @@ +/****************************************************************/ /** + * + * @file nbd_protocol.c + * + * @author Ronan Bignaux + * + * @brief Network Block Device Protocol server + * + * Copyright (c) Ronan Bignaux. 2021 + * All rights reserved. + * + ********************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification,are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Ronan Bignaux + * + */ + +#include "nbd_server.h" + +uint8_t nbd_buffer[NBD_BUFFER_LEN] __attribute__((aligned(64))); + +/** @ingroup nbd + * Fixed newstyle negotiation. + * @param client_socket + * @param ctx NBD callback struct + */ +struct nbd_context *negotiation_phase(int client_socket, struct nbd_context **ctxs) +{ + register int size; + uint32_t cflags, name_len, desc_len, len; + struct nbd_new_option new_opt; + struct nbd_export_name_option_reply handshake_finish; + struct nbd_fixed_new_option_reply fixed_new_option_reply; + struct nbd_new_handshake new_hs; + + //temporary workaround + struct nbd_context *ctx = ctxs[0]; + + /*** handshake ***/ + + new_hs.nbdmagic = htonll(NBD_MAGIC); + new_hs.version = htonll(NBD_NEW_VERSION); + new_hs.gflags = 0; //htons(NBD_FLAG_FIXED_NEWSTYLE); + size = nbd_send(client_socket, &new_hs, sizeof(struct nbd_new_handshake), + 0); + if (size < sizeof(struct nbd_new_handshake)) + goto error; + + size = nbd_recv(client_socket, &cflags, sizeof(cflags), 0); + if (size < sizeof(cflags)) + goto error; + cflags = htonl(cflags); + /* TODO: manage cflags + * https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#client-flags + */ + + ctx->eflags = NBD_FLAG_HAS_FLAGS; + + while (1) { + + /*** options haggling ***/ + + size = nbd_recv(client_socket, &new_opt, sizeof(struct nbd_new_option), + 0); + if (size < sizeof(struct nbd_new_option)) + goto error; + + new_opt.version = ntohll(new_opt.version); + if (new_opt.version != NBD_NEW_VERSION) + goto error; + + new_opt.option = htonl(new_opt.option); + new_opt.optlen = htonl(new_opt.optlen); + + if (new_opt.optlen > 0) { + size = nbd_recv(client_socket, &nbd_buffer, new_opt.optlen, 0); + nbd_buffer[new_opt.optlen] = '\0'; + } + + switch (new_opt.option) { + + case NBD_OPT_EXPORT_NAME: + + handshake_finish.exportsize = htonll(ctx->export_size); + handshake_finish.eflags = htons(ctx->eflags); + // TODO if NBD_FLAG_NO_ZEROES / NBD_FLAG_C_NO_ZEROES + memset(handshake_finish.zeroes, 0, sizeof(handshake_finish.zeroes)); + size = nbd_send(client_socket, &handshake_finish, + sizeof(struct nbd_export_name_option_reply), 0); + goto abort; + + case NBD_OPT_ABORT: + //TODO : test + fixed_new_option_reply.magic = htonll(NBD_REP_MAGIC); + fixed_new_option_reply.option = htonl(new_opt.option); + fixed_new_option_reply.reply = htonl(NBD_REP_ACK); + fixed_new_option_reply.replylen = 0; + size = nbd_send(client_socket, &fixed_new_option_reply, + sizeof(struct nbd_fixed_new_option_reply), 0); + goto soft_disconnect; + break; + // see nbdkit send_newstyle_option_reply_exportnames() + case NBD_OPT_LIST: + + name_len = strlen(ctx->export_name); + desc_len = ctx->export_desc ? strlen(ctx->export_desc) : 0; + len = htonl(name_len); + + //TODO : many export in a loop + fixed_new_option_reply.magic = htonll(NBD_REP_MAGIC); + fixed_new_option_reply.option = htonl(new_opt.option); + fixed_new_option_reply.reply = htonl(NBD_REP_SERVER); + fixed_new_option_reply.replylen = htonl(name_len + sizeof(len) + + desc_len); + + size = nbd_send(client_socket, &fixed_new_option_reply, + sizeof(struct nbd_fixed_new_option_reply), MSG_MORE); + size = nbd_send(client_socket, &len, sizeof len, MSG_MORE); + size = nbd_send(client_socket, ctx->export_name, name_len, MSG_MORE); + size = nbd_send(client_socket, ctx->export_desc, desc_len, 0); + break; + //TODO + // break; + // case NBD_OPT_STARTTLS: + // break; + // see nbdkit send_newstyle_option_reply_info_export() + case NBD_OPT_INFO: + case NBD_OPT_GO: + + + // if (new_opt.option == NBD_OPT_GO) + // goto abort; + // break; + case NBD_OPT_STRUCTURED_REPLY: + case NBD_OPT_LIST_META_CONTEXT: + case NBD_OPT_SET_META_CONTEXT: + default: + //TODO: test + fixed_new_option_reply.magic = htonll(NBD_REP_MAGIC); + fixed_new_option_reply.option = htonl(new_opt.option); + fixed_new_option_reply.reply = htonl(NBD_REP_ERR_UNSUP); + fixed_new_option_reply.replylen = 0; + size = nbd_send(client_socket, &fixed_new_option_reply, + sizeof(struct nbd_fixed_new_option_reply), 0); + break; + } + } + +abort: + return ctx; +soft_disconnect: +error: + return NULL; +} + +/** @ingroup nbd + * Transmission phase. + * @param client_socket + * @param ctx NBD callback struct + */ +int transmission_phase(int client_socket, struct nbd_context *ctx) +{ + register int r, size, error = -1, retry = NBD_MAX_RETRIES, sendflag = 0; + register uint32_t blkremains = 0, byteread = 0, bufbklsz = 0; + register uint64_t offset = 0; + struct nbd_simple_reply reply; + struct nbd_request request; + + reply.magic = htonl(NBD_SIMPLE_REPLY_MAGIC); + + while (1) { + + /*** requests handling ***/ + + // TODO : blocking here if no proper NBD_CMD_DISC, bad threading design ? + size = nbd_recv(client_socket, &request, sizeof(struct nbd_request), 0); + if (size < sizeof(struct nbd_request)) { + printf("lwNBD : sizeof NOK\n"); + goto error; + } + + // printf("lwNBD : sizeof OK.\n"); + + request.magic = ntohl(request.magic); + if (request.magic != NBD_REQUEST_MAGIC) { + printf("lwNBD : wrong NBD_REQUEST_MAGIC\n"); + goto error; + } + + request.flags = ntohs(request.flags); + request.type = ntohs(request.type); + request.offset = ntohll(request.offset); + request.count = ntohl(request.count); + + reply.handle = request.handle; + + // printf("lwNBD : entering NBD_CMD %d.\n", request.type); + + switch (request.type) { + + case NBD_CMD_READ: + + if (request.offset + request.count > ctx->export_size) + error = NBD_EIO; + else { + error = NBD_SUCCESS; + sendflag = MSG_MORE; + bufbklsz = NBD_BUFFER_LEN >> ctx->blockshift; + blkremains = request.count >> ctx->blockshift; + offset = request.offset >> ctx->blockshift; + byteread = bufbklsz << ctx->blockshift; + } + + reply.error = ntohl(error); + r = nbd_send(client_socket, &reply, sizeof(struct nbd_simple_reply), + sendflag); + + while (sendflag) { + + if (blkremains < bufbklsz) { + bufbklsz = blkremains; + byteread = bufbklsz << ctx->blockshift; + } + + if (blkremains <= bufbklsz) + sendflag = 0; + + r = ctx->read(ctx, ctx->buffer, offset, bufbklsz); + + if (r == 0) { + r = nbd_send(client_socket, ctx->buffer, byteread, sendflag); + if (r != byteread) + break; + offset += bufbklsz; + blkremains -= bufbklsz; + retry = NBD_MAX_RETRIES; + } else if (retry) { + retry--; + sendflag = 1; + } else { + printf("NBD_CMD_READ : EIO\n"); + goto error; // -EIO + // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE, ("nbd: error read\n")); + } + } + + break; + + case NBD_CMD_WRITE: + + if (ctx->eflags & NBD_FLAG_READ_ONLY) + error = NBD_EPERM; + else if (request.offset + request.count > ctx->export_size) + error = NBD_ENOSPC; + else { + error = NBD_SUCCESS; + sendflag = MSG_MORE; + bufbklsz = NBD_BUFFER_LEN >> ctx->blockshift; + blkremains = request.count >> ctx->blockshift; + offset = request.offset >> ctx->blockshift; + byteread = bufbklsz << ctx->blockshift; + } + + while (sendflag) { + + if (blkremains < bufbklsz) { + bufbklsz = blkremains; + byteread = bufbklsz << ctx->blockshift; + } + + if (blkremains <= bufbklsz) + sendflag = 0; + + r = nbd_recv(client_socket, ctx->buffer, byteread, 0); + + if (r == byteread) { + r = ctx->write(ctx, ctx->buffer, offset, bufbklsz); + if (r != 0) { + error = NBD_EIO; + sendflag = 0; + // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE, ("nbd: error read\n")); + } + offset += bufbklsz; + blkremains -= bufbklsz; + retry = NBD_MAX_RETRIES; + } else { + error = NBD_EOVERFLOW; //TODO + sendflag = 0; + // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE, ("nbd: error read\n")); + } + } + + reply.error = ntohl(error); + r = nbd_send(client_socket, &reply, sizeof(struct nbd_simple_reply), + 0); + + break; + + case NBD_CMD_DISC: + //TODO + goto soft_disconnect; + break; + + case NBD_CMD_FLUSH: + ctx->flush(ctx); + break; + + case NBD_CMD_TRIM: + case NBD_CMD_CACHE: + case NBD_CMD_WRITE_ZEROES: + case NBD_CMD_BLOCK_STATUS: + default: + /* NBD_REP_ERR_INVALID */ + goto error; + } + } + +soft_disconnect: + return NBD_SUCCESS; +error: + return error; +} diff --git a/modules/network/lwnbdsvr/lwNBD/nbd_protocol.h b/modules/network/lwnbdsvr/lwNBD/nbd_protocol.h new file mode 100644 index 000000000..e4dbf6b1a --- /dev/null +++ b/modules/network/lwnbdsvr/lwNBD/nbd_protocol.h @@ -0,0 +1,55 @@ +/****************************************************************/ /** + * + * @file nbd_protocol.h + * + * @author Ronan Bignaux + * + * @brief Network Block Device Protocol server + * + * Copyright (c) Ronan Bignaux. 2021 + * All rights reserved. + * + ********************************************************************/ + +/* + * Redistribution and use in source and binary forms, with or without + * modification,are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Ronan Bignaux + * + */ + +#ifndef LWIP_HDR_APPS_NBD_PROTOCOL_H +#define LWIP_HDR_APPS_NBD_PROTOCOL_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct nbd_context *negotiation_phase(int client_socket, struct nbd_context **ctxs); +int transmission_phase(int client_socket, struct nbd_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* LWIP_HDR_APPS_NBD_PROTOCOL_H */ diff --git a/modules/network/lwnbdsvr/lwNBD/nbd_server.c b/modules/network/lwnbdsvr/lwNBD/nbd_server.c index 5bcdff131..91a5ce23c 100644 --- a/modules/network/lwnbdsvr/lwNBD/nbd_server.c +++ b/modules/network/lwnbdsvr/lwNBD/nbd_server.c @@ -1,15 +1,15 @@ /****************************************************************/ /** - * - * @file nbd_server.c - * - * @author Ronan Bignaux - * - * @brief Network Block Device Protocol server - * - * Copyright (c) Ronan Bignaux. 2021 - * All rights reserved. - * - ********************************************************************/ + * + * @file nbd_server.c + * + * @author Ronan Bignaux + * + * @brief Network Block Device Protocol server + * + * Copyright (c) Ronan Bignaux. 2021 + * All rights reserved. + * + ********************************************************************/ /* * Redistribution and use in source and binary forms, with or without @@ -47,21 +47,20 @@ #include "nbd_server.h" -uint8_t nbd_buffer[NBD_BUFFER_LEN] __attribute__((aligned(64))); - /* * https://lwip.fandom.com/wiki/Receiving_data_with_LWIP */ -static int nbd_recv(int s, void *mem, size_t len, int flags) +int nbd_recv(int s, void *mem, size_t len, int flags) { uint32_t bytesRead = 0; uint32_t left = len; uint32_t totalRead = 0; - // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE("nbd_recv(-, 0x%X, %d)\n", (int)mem, size); + // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE("nbd_recv(-, 0x%X, %d)\n", (int)mem, size); + // dbgprintf("left = %u\n", left); do { bytesRead = recv(s, mem + totalRead, left, flags); - // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE("bytesRead = %d\n", bytesRead); + // dbgprintf("bytesRead = %u\n", bytesRead); if (bytesRead <= 0) break; @@ -72,299 +71,6 @@ static int nbd_recv(int s, void *mem, size_t len, int flags) return totalRead; } -/** @ingroup nbd - * Fixed newstyle negotiation. - * @param client_socket - * @param ctx NBD callback struct - */ -struct nbd_context *negotiation_phase(int client_socket, struct nbd_context **ctxs) -{ - register int size; - uint32_t cflags, name_len, desc_len, len; - struct nbd_new_option new_opt; - struct nbd_export_name_option_reply handshake_finish; - struct nbd_fixed_new_option_reply fixed_new_option_reply; - struct nbd_new_handshake new_hs; - - // temporary workaround - struct nbd_context *ctx = ctxs[0]; - - /*** handshake ***/ - - new_hs.nbdmagic = htonll(NBD_MAGIC); - new_hs.version = htonll(NBD_NEW_VERSION); - new_hs.gflags = 0; // htons(NBD_FLAG_FIXED_NEWSTYLE); - size = send(client_socket, &new_hs, sizeof(struct nbd_new_handshake), - 0); - if (size < sizeof(struct nbd_new_handshake)) - goto error; - - size = recv(client_socket, &cflags, sizeof(cflags), 0); - if (size < sizeof(cflags)) - goto error; - cflags = htonl(cflags); - /* TODO: manage cflags - * https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#client-flags - */ - - ctx->eflags = NBD_FLAG_HAS_FLAGS; - - while (1) { - - /*** options haggling ***/ - - size = recv(client_socket, &new_opt, sizeof(struct nbd_new_option), - 0); - if (size < sizeof(struct nbd_new_option)) - goto error; - - new_opt.version = ntohll(new_opt.version); - if (new_opt.version != NBD_NEW_VERSION) - goto error; - - new_opt.option = htonl(new_opt.option); - new_opt.optlen = htonl(new_opt.optlen); - - if (new_opt.optlen > 0) { - size = recv(client_socket, &nbd_buffer, new_opt.optlen, 0); - nbd_buffer[new_opt.optlen] = '\0'; - } - - switch (new_opt.option) { - - case NBD_OPT_EXPORT_NAME: - - handshake_finish.exportsize = htonll(ctx->export_size); - handshake_finish.eflags = htons(ctx->eflags); - // TODO if NBD_FLAG_NO_ZEROES / NBD_FLAG_C_NO_ZEROES - memset(handshake_finish.zeroes, 0, sizeof(handshake_finish.zeroes)); - size = send(client_socket, &handshake_finish, - sizeof(struct nbd_export_name_option_reply), 0); - goto abort; - - case NBD_OPT_ABORT: - // TODO : test - fixed_new_option_reply.magic = htonll(NBD_REP_MAGIC); - fixed_new_option_reply.option = htonl(new_opt.option); - fixed_new_option_reply.reply = htonl(NBD_REP_ACK); - fixed_new_option_reply.replylen = 0; - size = send(client_socket, &fixed_new_option_reply, - sizeof(struct nbd_fixed_new_option_reply), 0); - goto soft_disconnect; - break; - // see nbdkit send_newstyle_option_reply_exportnames() - case NBD_OPT_LIST: - - name_len = strlen(ctx->export_name); - desc_len = ctx->export_desc ? strlen(ctx->export_desc) : 0; - len = htonl(name_len); - - // TODO : many export in a loop - fixed_new_option_reply.magic = htonll(NBD_REP_MAGIC); - fixed_new_option_reply.option = htonl(new_opt.option); - fixed_new_option_reply.reply = htonl(NBD_REP_SERVER); - fixed_new_option_reply.replylen = htonl(name_len + sizeof(len) + - desc_len); - - size = send(client_socket, &fixed_new_option_reply, - sizeof(struct nbd_fixed_new_option_reply), MSG_MORE); - size = send(client_socket, &len, sizeof len, MSG_MORE); - size = send(client_socket, ctx->export_name, name_len, MSG_MORE); - size = send(client_socket, ctx->export_desc, desc_len, 0); - break; - // TODO - // break; - // case NBD_OPT_STARTTLS: - // break; - // see nbdkit send_newstyle_option_reply_info_export() - case NBD_OPT_INFO: - case NBD_OPT_GO: - - - // if (new_opt.option == NBD_OPT_GO) - // goto abort; - // break; - case NBD_OPT_STRUCTURED_REPLY: - case NBD_OPT_LIST_META_CONTEXT: - case NBD_OPT_SET_META_CONTEXT: - default: - // TODO: test - fixed_new_option_reply.magic = htonll(NBD_REP_MAGIC); - fixed_new_option_reply.option = htonl(new_opt.option); - fixed_new_option_reply.reply = htonl(NBD_REP_ERR_UNSUP); - fixed_new_option_reply.replylen = 0; - size = send(client_socket, &fixed_new_option_reply, - sizeof(struct nbd_fixed_new_option_reply), 0); - break; - } - } - -abort: - return ctx; -soft_disconnect: -error: - return NULL; -} - -/** @ingroup nbd - * Transmission phase. - * @param client_socket - * @param ctx NBD callback struct - */ -static int transmission_phase(int client_socket, struct nbd_context *ctx) -{ - register int r, size, error, retry = NBD_MAX_RETRIES, sendflag = 0; - register uint32_t blkremains = 0, byteread = 0, bufbklsz = 0; - register uint64_t offset = 0; - struct nbd_simple_reply reply; - struct nbd_request request; - - reply.magic = htonl(NBD_SIMPLE_REPLY_MAGIC); - - while (1) { - - /*** requests handling ***/ - - // TODO : blocking here if no proper NBD_CMD_DISC, bad threading design ? - size = recv(client_socket, &request, sizeof(struct nbd_request), 0); - if (size < sizeof(struct nbd_request)) - goto error; - - request.magic = ntohl(request.magic); - if (request.magic != NBD_REQUEST_MAGIC) - goto error; - - request.flags = ntohs(request.flags); - request.type = ntohs(request.type); - request.offset = ntohll(request.offset); - request.count = ntohl(request.count); - - reply.handle = request.handle; - - switch (request.type) { - - case NBD_CMD_READ: - - if (request.offset + request.count > ctx->export_size) - error = NBD_EIO; - else { - error = NBD_SUCCESS; - sendflag = MSG_MORE; - bufbklsz = NBD_BUFFER_LEN >> ctx->blockshift; - blkremains = request.count >> ctx->blockshift; - offset = request.offset >> ctx->blockshift; - byteread = bufbklsz << ctx->blockshift; - } - - reply.error = ntohl(error); - r = send(client_socket, &reply, sizeof(struct nbd_simple_reply), - sendflag); - - while (sendflag) { - - if (blkremains < bufbklsz) { - bufbklsz = blkremains; - byteread = bufbklsz << ctx->blockshift; - } - - if (blkremains <= bufbklsz) - sendflag = 0; - - r = ctx->read(ctx, ctx->buffer, offset, bufbklsz); - - if (r == 0) { - r = send(client_socket, ctx->buffer, byteread, sendflag); - if (r != byteread) - break; - offset += bufbklsz; - blkremains -= bufbklsz; - retry = NBD_MAX_RETRIES; - } else if (retry) { - retry--; - sendflag = 1; - } else { - goto error; // -EIO - // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE, ("nbd: error read\n")); - } - } - - break; - - case NBD_CMD_WRITE: - - if (ctx->eflags & NBD_FLAG_READ_ONLY) - error = NBD_EPERM; - else if (request.offset + request.count > ctx->export_size) - error = NBD_ENOSPC; - else { - error = NBD_SUCCESS; - sendflag = MSG_MORE; - bufbklsz = NBD_BUFFER_LEN >> ctx->blockshift; - blkremains = request.count >> ctx->blockshift; - offset = request.offset >> ctx->blockshift; - byteread = bufbklsz << ctx->blockshift; - } - - while (sendflag) { - - if (blkremains < bufbklsz) { - bufbklsz = blkremains; - byteread = bufbklsz << ctx->blockshift; - } - - if (blkremains <= bufbklsz) - sendflag = 0; - - r = nbd_recv(client_socket, ctx->buffer, byteread, 0); - - if (r == byteread) { - r = ctx->write(ctx, ctx->buffer, offset, bufbklsz); - if (r != 0) { - error = NBD_EIO; - sendflag = 0; - // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE, ("nbd: error read\n")); - } - offset += bufbklsz; - blkremains -= bufbklsz; - retry = NBD_MAX_RETRIES; - } else { - error = NBD_EOVERFLOW; // TODO - sendflag = 0; - // LWIP_DEBUGF(NBD_DEBUG | LWIP_DBG_STATE, ("nbd: error read\n")); - } - } - - reply.error = ntohl(error); - r = send(client_socket, &reply, sizeof(struct nbd_simple_reply), - 0); - - break; - - case NBD_CMD_DISC: - // TODO - goto soft_disconnect; - break; - - case NBD_CMD_FLUSH: - ctx->flush(ctx); - break; - - case NBD_CMD_TRIM: - case NBD_CMD_CACHE: - case NBD_CMD_WRITE_ZEROES: - case NBD_CMD_BLOCK_STATUS: - default: - /* NBD_REP_ERR_INVALID */ - goto error; - } - } - -soft_disconnect: - return 0; -error: - return -1; -} - /** @ingroup nbd * Initialize NBD server. * @param ctx NBD callback struct @@ -397,6 +103,8 @@ int nbd_init(struct nbd_context **ctx) while (1) { + printf("lwNBD: a new client connected.\n"); + addrlen = sizeof(peer); client_socket = accept(tcp_socket, (struct sockaddr *)&peer, &addrlen); @@ -405,13 +113,18 @@ int nbd_init(struct nbd_context **ctx) ctxt = negotiation_phase(client_socket, ctx); if (ctxt != NULL) - transmission_phase(client_socket, ctxt); + r = transmission_phase(client_socket, ctxt); + else + printf("lwNBD: handshake failed.\n"); + + if (r != NBD_SUCCESS) + printf("lwNBD: an error occured during transmission phase.\n"); closesocket(client_socket); } error: - printf("Error: failed to nbd_init"); + printf("lwNBD: failed to init server."); closesocket(tcp_socket); } } diff --git a/modules/network/lwnbdsvr/lwNBD/nbd_server.h b/modules/network/lwnbdsvr/lwNBD/nbd_server.h index 8150069f1..f82d27047 100644 --- a/modules/network/lwnbdsvr/lwNBD/nbd_server.h +++ b/modules/network/lwnbdsvr/lwNBD/nbd_server.h @@ -1,15 +1,15 @@ /****************************************************************/ /** - * - * @file nbd_server.h - * - * @author Ronan Bignaux - * - * @brief Network Block Device Protocol implementation options - * - * Copyright (c) Ronan Bignaux. 2021 - * All rights reserved. - * - ********************************************************************/ + * + * @file nbd_server.h + * + * @author Ronan Bignaux + * + * @brief Network Block Device Protocol implementation options + * + * Copyright (c) Ronan Bignaux. 2021 + * All rights reserved. + * + ********************************************************************/ /* * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,7 @@ #define LWIP_HDR_APPS_NBD_SERVER_H #include "nbd-protocol.h" +#include "nbd_protocol.h" #include "nbd_opts.h" //#include "lwip/apps/nbd_opts.h" @@ -57,14 +58,22 @@ //#include //#include -// TODO: Missing in PS2SDK -// pickup from https://gist.github.com/jtbr/7a43e6281e6cca353b33ee501421860c +#ifdef DEBUG +#define dbgprintf(args...) printf(args) +#else +#define dbgprintf(args...) \ + do { \ + } while (0) +#endif + +//TODO: Missing in PS2SDK +// pickup from https://gist.github.com/jtbr/7a43e6281e6cca353b33ee501421860c static inline uint64_t bswap64(uint64_t x) { return (((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) | ((x & 0x0000ff0000000000ull) >> 24) | ((x & 0x000000ff00000000ull) >> 8) | ((x & 0x00000000ff000000ull) << 8) | ((x & 0x0000000000ff0000ull) << 24) | ((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56)); } -// TODO: Missing in PS2SK's "common/include/tcpip.h" +//TODO: Missing in PS2SK's "common/include/tcpip.h" #if __BIG_ENDIAN__ #define htonll(x) (x) #define ntohll(x) (x) @@ -73,8 +82,8 @@ static inline uint64_t bswap64(uint64_t x) #define ntohll(x) bswap64(x) #endif -// TODO: Missing in PS2SK's , needed for "nbd-protocol.h" -// https://en.cppreference.com/w/c/types/integer +//TODO: Missing in PS2SK's , needed for "nbd-protocol.h" +// https://en.cppreference.com/w/c/types/integer #define UINT64_MAX 0xffffffffffffffff #define UINT64_C(x) ((x) + (UINT64_MAX - UINT64_MAX)) #endif @@ -83,8 +92,7 @@ static inline uint64_t bswap64(uint64_t x) extern "C" { #endif -// extern uint8_t buffer[]; -extern uint8_t nbd_buffer[NBD_BUFFER_LEN] __attribute__((aligned(64))); +extern uint8_t nbd_buffer[]; /** @ingroup nbd * NBD context containing callback functions for NBD transfers @@ -94,49 +102,54 @@ extern uint8_t nbd_buffer[NBD_BUFFER_LEN] __attribute__((aligned(64))); struct nbd_context { + // move in char export_name[32]; - char export_desc[64]; uint64_t export_size; /* size of export in byte */ uint16_t eflags; /* per-export flags */ - uint8_t blockshift; /* in power of 2 for bit shifting - log2(blocksize) */ + + char export_desc[64]; + uint8_t blockshift; /* in power of 2 for bit shifting - log2(blocksize) */ uint8_t *buffer; + /** - * block device - * @param - * @returns - */ + * block device + * @param + * @returns + */ int (*export_init)(struct nbd_context *me); /** - * Close block device handle - * @param handle File handle returned by open() - */ + * Close block device handle + * @param handle File handle returned by open() + */ // void (*close)(struct nbd_context *me); /** - * Read from block device - * @param - * @param buffer Target buffer to copy read data to - * @param offset Offset in block to copy read data to - * @param length Number of blocks to copy to buffer - * @returns >= 0: Success; < 0: Error - */ + * Read from block device + * @param + * @param buffer Target buffer to copy read data to + * @param offset Offset in block to copy read data to + * @param length Number of blocks to copy to buffer + * @returns >= 0: Success; < 0: Error + */ int (*read)(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length); /** - * Write to block device - * @param me () - * @param buffer Target buffer to copy write data to - * @param offset Offset in block to copy write data to - * @param length Number of blocks to copy to buffer - * @returns >= 0: Success; < 0: Error - */ + * Write to block device + * @param me () + * @param buffer Target buffer to copy write data to + * @param offset Offset in block to copy write data to + * @param length Number of blocks to copy to buffer + * @returns >= 0: Success; < 0: Error + */ int (*write)(struct nbd_context *me, void *buffer, uint64_t offset, uint32_t length); /** - * Flush to block device - * @param me () - * @returns >= 0: Success; < 0: Error - */ + * Flush to block device + * @param me () + * @returns >= 0: Success; < 0: Error + */ int (*flush)(struct nbd_context *me); }; +#define nbd_send(a, b, c, d) lwip_send(a, b, c, d) +int nbd_recv(int s, void *mem, size_t len, int flags); int nbd_init(struct nbd_context **ctx); #ifdef __cplusplus diff --git a/modules/network/lwnbdsvr/lwnbdsvr.c b/modules/network/lwnbdsvr/lwnbdsvr.c index 029e4447b..6c579ea01 100644 --- a/modules/network/lwnbdsvr/lwnbdsvr.c +++ b/modules/network/lwnbdsvr/lwnbdsvr.c @@ -11,11 +11,26 @@ extern struct irx_export_table _exp_lwnbdsvr; int _start(int argc, char **argv) { iop_thread_t nbd_thread; - - // TODO : platform specific block device detection then nbd_context initialization go here - // TODO : many export in a loop - if (nbd_contexts[0]->export_init(nbd_contexts[0]) != 0) + int successed_exported_ctx = 0; + struct nbd_context **ptr_ctx = nbd_contexts; + + //Platform specific block device detection then nbd_context initialization go here + while (*ptr_ctx) { + if ((*ptr_ctx)->export_init(*ptr_ctx) != 0) { + printf("lwnbdsvr: failed to init %s driver!\n", (*ptr_ctx)->export_name); + } else { + printf("lwnbdsvr: export %s\n", (*ptr_ctx)->export_desc); + successed_exported_ctx++; + } + ptr_ctx++; + } + + if (!successed_exported_ctx) { + printf("lwnbdsvr: nothing to export.\n"); return -1; + } + + printf("lwnbdsvr: init nbd_contexts ok.\n"); // register exports RegisterLibraryEntries(&_exp_lwnbdsvr); diff --git a/src/opl.c b/src/opl.c index a662f2a28..0d7875e37 100644 --- a/src/opl.c +++ b/src/opl.c @@ -1391,7 +1391,7 @@ static int loadLwnbdSvr(void) Clear it, otherwise it will get displayed after the server is closed. */ unloadPads(); - sysReset(0); + // sysReset(0); // usefull ? printf doesn't work with it. ret = ethLoadInitModules(); if (ret == 0) {