From 89a9fbead0d4242ca4e761d2525524396c2c7ff3 Mon Sep 17 00:00:00 2001 From: folkert van heusden Date: Wed, 22 Mar 2023 10:24:23 +0100 Subject: [PATCH] NBD: implemented read/write --- disk_backend_nbd.cpp | 175 +++++++++++++++++++++++++++++++++++++++++-- main.cpp | 20 ++++- utils.cpp | 47 ++++++++++++ utils.h | 3 + 4 files changed, 236 insertions(+), 9 deletions(-) diff --git a/disk_backend_nbd.cpp b/disk_backend_nbd.cpp index 0210ca8..eba79d7 100644 --- a/disk_backend_nbd.cpp +++ b/disk_backend_nbd.cpp @@ -1,9 +1,12 @@ #include +#include #include +#include #include #include "disk_backend_nbd.h" #include "log.h" +#include "utils.h" #ifdef ESP32 #include @@ -13,6 +16,9 @@ #include #endif +#define HTONLL(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32))) +#define NTOHLL(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32))) + disk_backend_nbd::disk_backend_nbd(const std::string & host, const int port) : host(host), @@ -80,6 +86,30 @@ bool disk_backend_nbd::connect(const bool retry) } freeaddrinfo(res); + + struct __attribute__ ((packed)) { + uint8_t magic1[8]; + uint8_t magic2[8]; + uint64_t size; + uint32_t flags; + uint8_t padding[124]; + } nbd_hello; + + if (fd != -1) { + if (READ(fd, reinterpret_cast(&nbd_hello), sizeof nbd_hello) != sizeof nbd_hello) { + close(fd); + fd = -1; + DOLOG(debug, false, "disk_backend_nbd::connect: connect short read"); + } + } + + if (memcmp(nbd_hello.magic1, "NBDMAGIC", 8) != 0) { + close(fd); + fd = -1; + DOLOG(debug, false, "disk_backend_nbd::connect: magic invalid"); + } + + DOLOG(info, false, "NBD size: %u", NTOHLL(nbd_hello.size)); } while(fd == -1 && retry); @@ -90,21 +120,150 @@ bool disk_backend_nbd::read(const off_t offset, const size_t n, uint8_t *const t { DOLOG(debug, false, "disk_backend_nbd::read: read %zu bytes from offset %zu", n, offset); - connect(true); + if (n == 0) + return true; - // TODO: loop dat als read() aangeeft dat de peer weg is, dat er dan gereconnect wordt - // anders return false - return pread(fd, target, n, offset) == ssize_t(n); + do { + if (fd == -1 && !connect(true)) { + DOLOG(debug, false, "disk_backend_nbd::read: (re-)connect"); + sleep(1); + continue; + } + + struct __attribute__ ((packed)) { + uint32_t magic; + uint32_t type; + uint64_t handle; + uint64_t offset; + uint32_t length; + } nbd_request { 0 }; + + nbd_request.magic = ntohl(0x25609513); + nbd_request.type = 0; // READ + nbd_request.offset = HTONLL(uint64_t(offset)); + nbd_request.length = htonl(n); + + if (WRITE(fd, reinterpret_cast(&nbd_request), sizeof nbd_request) != sizeof nbd_request) { + DOLOG(debug, false, "disk_backend_nbd::read: problem sending request"); + close(fd); + fd = -1; + sleep(1); + continue; + } + + struct __attribute__ ((packed)) { + uint32_t magic; + uint32_t error; + uint64_t handle; + } nbd_reply; + + if (READ(fd, reinterpret_cast(&nbd_reply), sizeof nbd_reply) != sizeof nbd_reply) { + DOLOG(debug, false, "disk_backend_nbd::read: problem receiving reply header"); + close(fd); + fd = -1; + sleep(1); + continue; + } + + if (ntohl(nbd_reply.magic) != 0x67446698) { + DOLOG(debug, false, "disk_backend_nbd::read: bad reply header %08x", nbd_reply.magic); + close(fd); + fd = -1; + sleep(1); + continue; + } + + int error = ntohl(nbd_reply.error); + if (error) { + DOLOG(debug, false, "disk_backend_nbd::read: NBD server indicated error: %d", error); + return false; + } + + if (READ(fd, reinterpret_cast(target), n) != ssize_t(n)) { + DOLOG(debug, false, "disk_backend_nbd::read: problem receiving payload"); + close(fd); + fd = -1; + sleep(1); + continue; + } + } + while(fd == -1); + + return true; } bool disk_backend_nbd::write(const off_t offset, const size_t n, const uint8_t *const from) { DOLOG(debug, false, "disk_backend_nbd::write: write %zu bytes to offset %zu", n, offset); - connect(true); + if (n == 0) + return true; - // TODO: loop dat als write() aangeeft dat de peer weg is, dat er dan gereconnect wordt - // anders return false + do { + if (!connect(true)) { + DOLOG(debug, false, "disk_backend_nbd::write: (re-)connect"); + sleep(1); + continue; + } - return pwrite(fd, from, n, offset) == ssize_t(n); + struct __attribute__ ((packed)) { + uint32_t magic; + uint32_t type; + uint64_t handle; + uint64_t offset; + uint32_t length; + } nbd_request { 0 }; + + nbd_request.magic = ntohl(0x25609513); + nbd_request.type = 1; // WRITE + nbd_request.offset = HTONLL(uint64_t(offset)); + nbd_request.length = htonl(n); + + if (WRITE(fd, reinterpret_cast(&nbd_request), sizeof nbd_request) != sizeof nbd_request) { + DOLOG(debug, false, "disk_backend_nbd::write: problem sending request"); + close(fd); + fd = -1; + sleep(1); + continue; + } + + if (WRITE(fd, reinterpret_cast(from), n) != ssize_t(n)) { + DOLOG(debug, false, "disk_backend_nbd::write: problem sending payload"); + close(fd); + fd = -1; + sleep(1); + continue; + } + + struct __attribute__ ((packed)) { + uint32_t magic; + uint32_t error; + uint64_t handle; + } nbd_reply; + + if (READ(fd, reinterpret_cast(&nbd_reply), sizeof nbd_reply) != sizeof nbd_reply) { + DOLOG(debug, false, "disk_backend_nbd::write: problem receiving reply header"); + close(fd); + fd = -1; + sleep(1); + continue; + } + + if (ntohl(nbd_reply.magic) != 0x67446698) { + DOLOG(debug, false, "disk_backend_nbd::write: bad reply header %08x", nbd_reply.magic); + close(fd); + fd = -1; + sleep(1); + continue; + } + + int error = ntohl(nbd_reply.error); + if (error) { + DOLOG(debug, false, "disk_backend_nbd::write: NBD server indicated error: %d", error); + return false; + } + } + while(fd == -1); + + return true; } diff --git a/main.cpp b/main.cpp index 8746e82..c2fef1b 100644 --- a/main.cpp +++ b/main.cpp @@ -14,6 +14,7 @@ #include "debugger.h" #include "disk_backend.h" #include "disk_backend_file.h" +#include "disk_backend_nbd.h" #include "gen.h" #include "kw11-l.h" #include "loaders.h" @@ -47,6 +48,7 @@ void help() printf("-T t.bin load file as a binary tape file (like simh \"load\" command)\n"); printf("-R d.rk load file as a RK05 disk device\n"); printf("-r d.rl load file as a RL02 disk device\n"); + printf("-N host:port:type use NBD-server as disk device, type being either \"rk05\" or \"rl02\"\n"); printf("-p 123 set CPU start pointer to decimal(!) value\n"); printf("-b x enable bootloader (build-in), parameter must be \"rk05\" or \"rl02\"\n"); printf("-n ncurses UI\n"); @@ -85,7 +87,7 @@ int main(int argc, char *argv[]) disk_backend *temp_d = nullptr; int opt = -1; - while((opt = getopt(argc, argv, "hm:T:r:R:p:ndtL:b:l:s:Q:")) != -1) + while((opt = getopt(argc, argv, "hm:T:r:R:p:ndtL:b:l:s:Q:N:")) != -1) { switch(opt) { case 'h': @@ -149,6 +151,22 @@ int main(int argc, char *argv[]) rl02_files.push_back(temp_d); break; + case 'N': { + auto parts = split(optarg, ":"); + if (parts.size() != 3) + error_exit(false, "-N: parameter missing"); + + temp_d = new disk_backend_nbd(parts.at(0), atoi(parts.at(1).c_str())); + + if (parts.at(2) == "rk05") + rk05_files.push_back(temp_d); + else if (parts.at(2) == "rl02") + rl02_files.push_back(temp_d); + else + error_exit(false, "\"%s\" is not recognized as a disk type", parts.at(2).c_str()); + } + break; + case 'p': start_addr = atoi(optarg); sa_set = true; diff --git a/utils.cpp b/utils.cpp index c026511..f744e3f 100644 --- a/utils.cpp +++ b/utils.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -144,3 +145,49 @@ void set_thread_name(std::string name) pthread_setname_np(pthread_self(), name.c_str()); #endif } + +ssize_t WRITE(int fd, const char *whereto, size_t len) +{ + ssize_t cnt=0; + + while(len > 0) + { + ssize_t rc = write(fd, whereto, len); + + if (rc == -1) + return -1; + else if (rc == 0) + return -1; + else + { + whereto += rc; + len -= rc; + cnt += rc; + } + } + + return cnt; +} + +ssize_t READ(int fd, char *whereto, size_t len) +{ + ssize_t cnt=0; + + while(len > 0) + { + ssize_t rc = read(fd, whereto, len); + + if (rc == -1) + return -1; + else if (rc == 0) + break; + else + { + whereto += rc; + len -= rc; + cnt += rc; + } + } + + return cnt; +} diff --git a/utils.h b/utils.h index 15eee2b..25c6bd3 100644 --- a/utils.h +++ b/utils.h @@ -18,3 +18,6 @@ uint64_t get_us(); void myusleep(uint64_t us); void set_thread_name(std::string name); + +ssize_t WRITE(int fd, const char *whereto, size_t len); +ssize_t READ(int fd, char *whereto, size_t len);