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