19980540bSIngo Weinhold /*
2965b10ccSAxel Dörfler * Copyright 2005-2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
39980540bSIngo Weinhold * All rights reserved. Distributed under the terms of the MIT License.
49980540bSIngo Weinhold */
59980540bSIngo Weinhold
69980540bSIngo Weinhold #include <endian.h>
79980540bSIngo Weinhold #include <errno.h>
89980540bSIngo Weinhold #include <fcntl.h>
92a39e9e4SIngo Weinhold #include <inttypes.h>
109980540bSIngo Weinhold #include <stdio.h>
119980540bSIngo Weinhold #include <stdlib.h>
129980540bSIngo Weinhold #include <string.h>
139980540bSIngo Weinhold #include <unistd.h>
149980540bSIngo Weinhold #include <netinet/in.h>
159980540bSIngo Weinhold #include <sys/socket.h>
169980540bSIngo Weinhold #include <sys/stat.h>
179980540bSIngo Weinhold
189980540bSIngo Weinhold #include <boot/net/RemoteDiskDefs.h>
199980540bSIngo Weinhold
202a39e9e4SIngo Weinhold
21965b10ccSAxel Dörfler #if defined(__BEOS__) && !defined(__HAIKU__)
22965b10ccSAxel Dörfler typedef int socklen_t;
23965b10ccSAxel Dörfler #endif
24965b10ccSAxel Dörfler
259980540bSIngo Weinhold
269980540bSIngo Weinhold #if __BYTE_ORDER == __LITTLE_ENDIAN
279980540bSIngo Weinhold
289980540bSIngo Weinhold static inline
swap_uint64(uint64_t data)299980540bSIngo Weinhold uint64_t swap_uint64(uint64_t data)
309980540bSIngo Weinhold {
319980540bSIngo Weinhold return ((data & 0xff) << 56)
329980540bSIngo Weinhold | ((data & 0xff00) << 40)
339980540bSIngo Weinhold | ((data & 0xff0000) << 24)
349980540bSIngo Weinhold | ((data & 0xff000000) << 8)
359980540bSIngo Weinhold | ((data >> 8) & 0xff000000)
369980540bSIngo Weinhold | ((data >> 24) & 0xff0000)
379980540bSIngo Weinhold | ((data >> 40) & 0xff00)
389980540bSIngo Weinhold | ((data >> 56) & 0xff);
399980540bSIngo Weinhold }
409980540bSIngo Weinhold
419980540bSIngo Weinhold #define host_to_net64(data) swap_uint64(data)
429980540bSIngo Weinhold #define net_to_host64(data) swap_uint64(data)
439980540bSIngo Weinhold
449980540bSIngo Weinhold #endif
459980540bSIngo Weinhold
469980540bSIngo Weinhold #if __BYTE_ORDER == __BIG_ENDIAN
479980540bSIngo Weinhold #define host_to_net64(data) (data)
489980540bSIngo Weinhold #define net_to_host64(data) (data)
499980540bSIngo Weinhold #endif
509980540bSIngo Weinhold
519980540bSIngo Weinhold #undef htonll
529980540bSIngo Weinhold #undef ntohll
539980540bSIngo Weinhold #define htonll(data) host_to_net64(data)
549980540bSIngo Weinhold #define ntohll(data) net_to_host64(data)
559980540bSIngo Weinhold
569980540bSIngo Weinhold
579980540bSIngo Weinhold class Server {
589980540bSIngo Weinhold public:
Server(const char * fileName)599980540bSIngo Weinhold Server(const char *fileName)
609980540bSIngo Weinhold : fImagePath(fileName),
619980540bSIngo Weinhold fImageFD(-1),
629980540bSIngo Weinhold fImageSize(0),
639980540bSIngo Weinhold fSocket(-1)
649980540bSIngo Weinhold {
659980540bSIngo Weinhold }
669980540bSIngo Weinhold
Run()679980540bSIngo Weinhold int Run()
689980540bSIngo Weinhold {
699980540bSIngo Weinhold _CreateSocket();
709980540bSIngo Weinhold
719980540bSIngo Weinhold // main server loop
729980540bSIngo Weinhold for (;;) {
739980540bSIngo Weinhold // receive
749980540bSIngo Weinhold fClientAddress.sin_family = AF_INET;
759980540bSIngo Weinhold fClientAddress.sin_port = 0;
769980540bSIngo Weinhold fClientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
779980540bSIngo Weinhold socklen_t addrSize = sizeof(fClientAddress);
789980540bSIngo Weinhold char buffer[2048];
799980540bSIngo Weinhold ssize_t bytesRead = recvfrom(fSocket, buffer, sizeof(buffer), 0,
809980540bSIngo Weinhold (sockaddr*)&fClientAddress, &addrSize);
819980540bSIngo Weinhold // handle error
829980540bSIngo Weinhold if (bytesRead < 0) {
839980540bSIngo Weinhold if (errno == EINTR)
849980540bSIngo Weinhold continue;
859980540bSIngo Weinhold fprintf(stderr, "Error: Failed to read from socket: %s.\n",
869980540bSIngo Weinhold strerror(errno));
879980540bSIngo Weinhold exit(1);
889980540bSIngo Weinhold }
899980540bSIngo Weinhold
909980540bSIngo Weinhold // short package?
919980540bSIngo Weinhold if (bytesRead < (ssize_t)sizeof(remote_disk_header)) {
929980540bSIngo Weinhold fprintf(stderr, "Dropping short request package (%d bytes).\n",
932a39e9e4SIngo Weinhold (int)bytesRead);
949980540bSIngo Weinhold continue;
959980540bSIngo Weinhold }
969980540bSIngo Weinhold
979980540bSIngo Weinhold fRequest = (remote_disk_header*)buffer;
989980540bSIngo Weinhold fRequestSize = bytesRead;
999980540bSIngo Weinhold
1009980540bSIngo Weinhold switch (fRequest->command) {
1019980540bSIngo Weinhold case REMOTE_DISK_HELLO_REQUEST:
1029980540bSIngo Weinhold _HandleHelloRequest();
1039980540bSIngo Weinhold break;
1049980540bSIngo Weinhold
1059980540bSIngo Weinhold case REMOTE_DISK_READ_REQUEST:
1069980540bSIngo Weinhold _HandleReadRequest();
1079980540bSIngo Weinhold break;
1089980540bSIngo Weinhold
1099980540bSIngo Weinhold case REMOTE_DISK_WRITE_REQUEST:
1109980540bSIngo Weinhold _HandleWriteRequest();
1119980540bSIngo Weinhold break;
1129980540bSIngo Weinhold
1139980540bSIngo Weinhold default:
1149980540bSIngo Weinhold fprintf(stderr, "Ignoring invalid request %d.\n",
1159980540bSIngo Weinhold (int)fRequest->command);
1169980540bSIngo Weinhold break;
1179980540bSIngo Weinhold }
1189980540bSIngo Weinhold }
1199980540bSIngo Weinhold
1209980540bSIngo Weinhold return 0;
1219980540bSIngo Weinhold }
1229980540bSIngo Weinhold
1239980540bSIngo Weinhold private:
_OpenImage(bool reopen)1245615edfdSIngo Weinhold void _OpenImage(bool reopen)
1259980540bSIngo Weinhold {
1265615edfdSIngo Weinhold // already open?
1275615edfdSIngo Weinhold if (fImageFD >= 0) {
1285615edfdSIngo Weinhold if (!reopen)
1295615edfdSIngo Weinhold return;
1305615edfdSIngo Weinhold
1315615edfdSIngo Weinhold close(fImageFD);
1325615edfdSIngo Weinhold fImageFD = -1;
1335615edfdSIngo Weinhold fImageSize = 0;
1345615edfdSIngo Weinhold }
1355615edfdSIngo Weinhold
1369980540bSIngo Weinhold // open the image
1379980540bSIngo Weinhold fImageFD = open(fImagePath, O_RDWR);
1389980540bSIngo Weinhold if (fImageFD < 0) {
1399980540bSIngo Weinhold fprintf(stderr, "Error: Failed to open \"%s\": %s.\n", fImagePath,
1409980540bSIngo Weinhold strerror(errno));
1419980540bSIngo Weinhold exit(1);
1429980540bSIngo Weinhold }
1439980540bSIngo Weinhold
1449980540bSIngo Weinhold // get its size
1459980540bSIngo Weinhold struct stat st;
1469980540bSIngo Weinhold if (fstat(fImageFD, &st) < 0) {
1479980540bSIngo Weinhold fprintf(stderr, "Error: Failed to stat \"%s\": %s.\n", fImagePath,
1489980540bSIngo Weinhold strerror(errno));
1499980540bSIngo Weinhold exit(1);
1509980540bSIngo Weinhold }
1519980540bSIngo Weinhold fImageSize = st.st_size;
1529980540bSIngo Weinhold }
1539980540bSIngo Weinhold
_CreateSocket()1549980540bSIngo Weinhold void _CreateSocket()
1559980540bSIngo Weinhold {
1569980540bSIngo Weinhold // create a socket
1579980540bSIngo Weinhold fSocket = socket(AF_INET, SOCK_DGRAM, 0);
1589980540bSIngo Weinhold if (fSocket < 0) {
1599980540bSIngo Weinhold fprintf(stderr, "Error: Failed to create a socket: %s.",
1609980540bSIngo Weinhold strerror(errno));
1619980540bSIngo Weinhold exit(1);
1629980540bSIngo Weinhold }
1639980540bSIngo Weinhold
1649980540bSIngo Weinhold // bind it to the port
1659980540bSIngo Weinhold sockaddr_in addr;
1669980540bSIngo Weinhold addr.sin_family = AF_INET;
1679980540bSIngo Weinhold addr.sin_port = htons(REMOTE_DISK_SERVER_PORT);
1689980540bSIngo Weinhold addr.sin_addr.s_addr = INADDR_ANY;
1699980540bSIngo Weinhold if (bind(fSocket, (sockaddr*)&addr, sizeof(addr)) < 0) {
1709980540bSIngo Weinhold fprintf(stderr, "Error: Failed to bind socket to port %hu: %s\n",
1719980540bSIngo Weinhold REMOTE_DISK_SERVER_PORT, strerror(errno));
1729980540bSIngo Weinhold exit(1);
1739980540bSIngo Weinhold }
1749980540bSIngo Weinhold }
1759980540bSIngo Weinhold
_HandleHelloRequest()1769980540bSIngo Weinhold void _HandleHelloRequest()
1779980540bSIngo Weinhold {
1789980540bSIngo Weinhold printf("HELLO request\n");
1799980540bSIngo Weinhold
1805615edfdSIngo Weinhold _OpenImage(true);
1815615edfdSIngo Weinhold
1829980540bSIngo Weinhold remote_disk_header reply;
1839980540bSIngo Weinhold reply.offset = htonll(fImageSize);
1849980540bSIngo Weinhold
1859980540bSIngo Weinhold reply.command = REMOTE_DISK_HELLO_REPLY;
1869980540bSIngo Weinhold _SendReply(&reply, sizeof(remote_disk_header));
1879980540bSIngo Weinhold }
1889980540bSIngo Weinhold
_HandleReadRequest()1899980540bSIngo Weinhold void _HandleReadRequest()
1909980540bSIngo Weinhold {
1915615edfdSIngo Weinhold _OpenImage(false);
1925615edfdSIngo Weinhold
1939980540bSIngo Weinhold char buffer[2048];
1949980540bSIngo Weinhold remote_disk_header *reply = (remote_disk_header*)buffer;
1959980540bSIngo Weinhold uint64_t offset = ntohll(fRequest->offset);
1969980540bSIngo Weinhold int16_t size = ntohs(fRequest->size);
1979980540bSIngo Weinhold int16_t result = 0;
1989980540bSIngo Weinhold
199*6ce8dc88SAlexander von Gluck IV printf("READ request: offset: %" PRIu64 ", %hd bytes\n", offset, size);
2009980540bSIngo Weinhold
2019980540bSIngo Weinhold if (offset < (uint64_t)fImageSize && size > 0) {
2029980540bSIngo Weinhold // always read 1024 bytes
2039980540bSIngo Weinhold size = REMOTE_DISK_BLOCK_SIZE;
2049980540bSIngo Weinhold if (offset + size > (uint64_t)fImageSize)
2059980540bSIngo Weinhold size = fImageSize - offset;
2069980540bSIngo Weinhold
2079980540bSIngo Weinhold // seek to the offset
2089980540bSIngo Weinhold off_t oldOffset = lseek(fImageFD, offset, SEEK_SET);
2099980540bSIngo Weinhold if (oldOffset >= 0) {
2109980540bSIngo Weinhold // read
2119980540bSIngo Weinhold ssize_t bytesRead = read(fImageFD, reply->data, size);
2129980540bSIngo Weinhold if (bytesRead >= 0) {
2139980540bSIngo Weinhold result = bytesRead;
2149980540bSIngo Weinhold } else {
215*6ce8dc88SAlexander von Gluck IV fprintf(stderr, "Error: Failed to read at position %" PRIu64 ": "
2169980540bSIngo Weinhold "%s.", offset, strerror(errno));
2179980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR;
2189980540bSIngo Weinhold }
2199980540bSIngo Weinhold } else {
220*6ce8dc88SAlexander von Gluck IV fprintf(stderr, "Error: Failed to seek to position %" PRIu64 ": %s.",
2219980540bSIngo Weinhold offset, strerror(errno));
2229980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR;
2239980540bSIngo Weinhold }
2249980540bSIngo Weinhold }
2259980540bSIngo Weinhold
2269980540bSIngo Weinhold // send reply
2279980540bSIngo Weinhold reply->command = REMOTE_DISK_READ_REPLY;
2289980540bSIngo Weinhold reply->offset = htonll(offset);
2299980540bSIngo Weinhold reply->size = htons(result);
2309980540bSIngo Weinhold _SendReply(reply, sizeof(*reply) + (result >= 0 ? result : 0));
2319980540bSIngo Weinhold }
2329980540bSIngo Weinhold
_HandleWriteRequest()2339980540bSIngo Weinhold void _HandleWriteRequest()
2349980540bSIngo Weinhold {
2355615edfdSIngo Weinhold _OpenImage(false);
2365615edfdSIngo Weinhold
2379980540bSIngo Weinhold remote_disk_header reply;
2389980540bSIngo Weinhold uint64_t offset = ntohll(fRequest->offset);
2399980540bSIngo Weinhold int16_t size = ntohs(fRequest->size);
2409980540bSIngo Weinhold int16_t result = 0;
2419980540bSIngo Weinhold
242*6ce8dc88SAlexander von Gluck IV printf("WRITE request: offset: %" PRIu64 ", %hd bytes\n", offset, size);
2439980540bSIngo Weinhold
2449980540bSIngo Weinhold if (size < 0
2459980540bSIngo Weinhold || (uint32_t)size > fRequestSize - sizeof(remote_disk_header)
2469980540bSIngo Weinhold || offset > (uint64_t)fImageSize) {
2479980540bSIngo Weinhold result = REMOTE_DISK_BAD_REQUEST;
2489980540bSIngo Weinhold } else if (offset < (uint64_t)fImageSize && size > 0) {
2499980540bSIngo Weinhold if (offset + size > (uint64_t)fImageSize)
2509980540bSIngo Weinhold size = fImageSize - offset;
2519980540bSIngo Weinhold
2529980540bSIngo Weinhold // seek to the offset
2539980540bSIngo Weinhold off_t oldOffset = lseek(fImageFD, offset, SEEK_SET);
2549980540bSIngo Weinhold if (oldOffset >= 0) {
2559980540bSIngo Weinhold // write
2569980540bSIngo Weinhold ssize_t bytesWritten = write(fImageFD, fRequest->data, size);
2579980540bSIngo Weinhold if (bytesWritten >= 0) {
2589980540bSIngo Weinhold result = bytesWritten;
2599980540bSIngo Weinhold } else {
260*6ce8dc88SAlexander von Gluck IV fprintf(stderr, "Error: Failed to write at position %" PRIu64 ": "
2619980540bSIngo Weinhold "%s.", offset, strerror(errno));
2629980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR;
2639980540bSIngo Weinhold }
2649980540bSIngo Weinhold } else {
265*6ce8dc88SAlexander von Gluck IV fprintf(stderr, "Error: Failed to seek to position %" PRIu64 ": %s.",
2669980540bSIngo Weinhold offset, strerror(errno));
2679980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR;
2689980540bSIngo Weinhold }
2699980540bSIngo Weinhold }
2709980540bSIngo Weinhold
2719980540bSIngo Weinhold // send reply
2729980540bSIngo Weinhold reply.command = REMOTE_DISK_WRITE_REPLY;
2739980540bSIngo Weinhold reply.offset = htonll(offset);
2749980540bSIngo Weinhold reply.size = htons(result);
2759980540bSIngo Weinhold _SendReply(&reply, sizeof(reply));
2769980540bSIngo Weinhold }
2779980540bSIngo Weinhold
_SendReply(remote_disk_header * reply,int size)2789980540bSIngo Weinhold void _SendReply(remote_disk_header *reply, int size)
2799980540bSIngo Weinhold {
2809980540bSIngo Weinhold reply->request_id = fRequest->request_id;
2819980540bSIngo Weinhold reply->port = htons(REMOTE_DISK_SERVER_PORT);
2829980540bSIngo Weinhold
2839980540bSIngo Weinhold for (;;) {
2849980540bSIngo Weinhold ssize_t bytesSent = sendto(fSocket, reply, size, 0,
2859980540bSIngo Weinhold (const sockaddr*)&fClientAddress, sizeof(fClientAddress));
2869980540bSIngo Weinhold
2879980540bSIngo Weinhold if (bytesSent < 0) {
2889980540bSIngo Weinhold if (errno == EINTR)
2899980540bSIngo Weinhold continue;
2909980540bSIngo Weinhold fprintf(stderr, "Error: Failed to send reply to client: %s.\n",
2919980540bSIngo Weinhold strerror(errno));
2929980540bSIngo Weinhold }
2939980540bSIngo Weinhold break;
2949980540bSIngo Weinhold }
2959980540bSIngo Weinhold }
2969980540bSIngo Weinhold
2979980540bSIngo Weinhold private:
2989980540bSIngo Weinhold const char *fImagePath;
2999980540bSIngo Weinhold int fImageFD;
3009980540bSIngo Weinhold off_t fImageSize;
3019980540bSIngo Weinhold int fSocket;
3029980540bSIngo Weinhold remote_disk_header *fRequest;
3039980540bSIngo Weinhold ssize_t fRequestSize;
3049980540bSIngo Weinhold sockaddr_in fClientAddress;
3059980540bSIngo Weinhold };
3069980540bSIngo Weinhold
3079980540bSIngo Weinhold
3089980540bSIngo Weinhold // main
3099980540bSIngo Weinhold int
main(int argc,const char * const * argv)3109980540bSIngo Weinhold main(int argc, const char *const *argv)
3119980540bSIngo Weinhold {
3129980540bSIngo Weinhold if (argc != 2) {
3139980540bSIngo Weinhold fprintf(stderr, "Usage: %s <image path>\n", argv[0]);
3149980540bSIngo Weinhold exit(1);
3159980540bSIngo Weinhold }
3169980540bSIngo Weinhold const char *fileName = argv[1];
3179980540bSIngo Weinhold
3189980540bSIngo Weinhold Server server(fileName);
3199980540bSIngo Weinhold return server.Run();
3209980540bSIngo Weinhold }
321