1*9980540bSIngo Weinhold /* 2*9980540bSIngo Weinhold * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3*9980540bSIngo Weinhold * All rights reserved. Distributed under the terms of the MIT License. 4*9980540bSIngo Weinhold */ 5*9980540bSIngo Weinhold 6*9980540bSIngo Weinhold #include <endian.h> 7*9980540bSIngo Weinhold #include <errno.h> 8*9980540bSIngo Weinhold #include <fcntl.h> 9*9980540bSIngo Weinhold #include <stdio.h> 10*9980540bSIngo Weinhold #include <stdlib.h> 11*9980540bSIngo Weinhold #include <stdint.h> 12*9980540bSIngo Weinhold #include <string.h> 13*9980540bSIngo Weinhold #include <unistd.h> 14*9980540bSIngo Weinhold #include <netinet/in.h> 15*9980540bSIngo Weinhold #include <sys/socket.h> 16*9980540bSIngo Weinhold #include <sys/stat.h> 17*9980540bSIngo Weinhold 18*9980540bSIngo Weinhold #include <boot/net/RemoteDiskDefs.h> 19*9980540bSIngo Weinhold 20*9980540bSIngo Weinhold 21*9980540bSIngo Weinhold #if __BYTE_ORDER == __LITTLE_ENDIAN 22*9980540bSIngo Weinhold 23*9980540bSIngo Weinhold static inline 24*9980540bSIngo Weinhold uint64_t swap_uint64(uint64_t data) 25*9980540bSIngo Weinhold { 26*9980540bSIngo Weinhold return ((data & 0xff) << 56) 27*9980540bSIngo Weinhold | ((data & 0xff00) << 40) 28*9980540bSIngo Weinhold | ((data & 0xff0000) << 24) 29*9980540bSIngo Weinhold | ((data & 0xff000000) << 8) 30*9980540bSIngo Weinhold | ((data >> 8) & 0xff000000) 31*9980540bSIngo Weinhold | ((data >> 24) & 0xff0000) 32*9980540bSIngo Weinhold | ((data >> 40) & 0xff00) 33*9980540bSIngo Weinhold | ((data >> 56) & 0xff); 34*9980540bSIngo Weinhold } 35*9980540bSIngo Weinhold 36*9980540bSIngo Weinhold #define host_to_net64(data) swap_uint64(data) 37*9980540bSIngo Weinhold #define net_to_host64(data) swap_uint64(data) 38*9980540bSIngo Weinhold 39*9980540bSIngo Weinhold #endif 40*9980540bSIngo Weinhold 41*9980540bSIngo Weinhold #if __BYTE_ORDER == __BIG_ENDIAN 42*9980540bSIngo Weinhold #define host_to_net64(data) (data) 43*9980540bSIngo Weinhold #define net_to_host64(data) (data) 44*9980540bSIngo Weinhold #endif 45*9980540bSIngo Weinhold 46*9980540bSIngo Weinhold #undef htonll 47*9980540bSIngo Weinhold #undef ntohll 48*9980540bSIngo Weinhold #define htonll(data) host_to_net64(data) 49*9980540bSIngo Weinhold #define ntohll(data) net_to_host64(data) 50*9980540bSIngo Weinhold 51*9980540bSIngo Weinhold 52*9980540bSIngo Weinhold class Server { 53*9980540bSIngo Weinhold public: 54*9980540bSIngo Weinhold Server(const char *fileName) 55*9980540bSIngo Weinhold : fImagePath(fileName), 56*9980540bSIngo Weinhold fImageFD(-1), 57*9980540bSIngo Weinhold fImageSize(0), 58*9980540bSIngo Weinhold fSocket(-1) 59*9980540bSIngo Weinhold { 60*9980540bSIngo Weinhold } 61*9980540bSIngo Weinhold 62*9980540bSIngo Weinhold int Run() 63*9980540bSIngo Weinhold { 64*9980540bSIngo Weinhold _OpenImage(); 65*9980540bSIngo Weinhold _CreateSocket(); 66*9980540bSIngo Weinhold 67*9980540bSIngo Weinhold // main server loop 68*9980540bSIngo Weinhold for (;;) { 69*9980540bSIngo Weinhold // receive 70*9980540bSIngo Weinhold fClientAddress.sin_family = AF_INET; 71*9980540bSIngo Weinhold fClientAddress.sin_port = 0; 72*9980540bSIngo Weinhold fClientAddress.sin_addr.s_addr = htonl(INADDR_ANY); 73*9980540bSIngo Weinhold socklen_t addrSize = sizeof(fClientAddress); 74*9980540bSIngo Weinhold char buffer[2048]; 75*9980540bSIngo Weinhold ssize_t bytesRead = recvfrom(fSocket, buffer, sizeof(buffer), 0, 76*9980540bSIngo Weinhold (sockaddr*)&fClientAddress, &addrSize); 77*9980540bSIngo Weinhold // handle error 78*9980540bSIngo Weinhold if (bytesRead < 0) { 79*9980540bSIngo Weinhold if (errno == EINTR) 80*9980540bSIngo Weinhold continue; 81*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to read from socket: %s.\n", 82*9980540bSIngo Weinhold strerror(errno)); 83*9980540bSIngo Weinhold exit(1); 84*9980540bSIngo Weinhold } 85*9980540bSIngo Weinhold 86*9980540bSIngo Weinhold // short package? 87*9980540bSIngo Weinhold if (bytesRead < (ssize_t)sizeof(remote_disk_header)) { 88*9980540bSIngo Weinhold fprintf(stderr, "Dropping short request package (%d bytes).\n", 89*9980540bSIngo Weinhold bytesRead); 90*9980540bSIngo Weinhold continue; 91*9980540bSIngo Weinhold } 92*9980540bSIngo Weinhold 93*9980540bSIngo Weinhold fRequest = (remote_disk_header*)buffer; 94*9980540bSIngo Weinhold fRequestSize = bytesRead; 95*9980540bSIngo Weinhold 96*9980540bSIngo Weinhold switch (fRequest->command) { 97*9980540bSIngo Weinhold case REMOTE_DISK_HELLO_REQUEST: 98*9980540bSIngo Weinhold _HandleHelloRequest(); 99*9980540bSIngo Weinhold break; 100*9980540bSIngo Weinhold 101*9980540bSIngo Weinhold case REMOTE_DISK_READ_REQUEST: 102*9980540bSIngo Weinhold _HandleReadRequest(); 103*9980540bSIngo Weinhold break; 104*9980540bSIngo Weinhold 105*9980540bSIngo Weinhold case REMOTE_DISK_WRITE_REQUEST: 106*9980540bSIngo Weinhold _HandleWriteRequest(); 107*9980540bSIngo Weinhold break; 108*9980540bSIngo Weinhold 109*9980540bSIngo Weinhold default: 110*9980540bSIngo Weinhold fprintf(stderr, "Ignoring invalid request %d.\n", 111*9980540bSIngo Weinhold (int)fRequest->command); 112*9980540bSIngo Weinhold break; 113*9980540bSIngo Weinhold } 114*9980540bSIngo Weinhold } 115*9980540bSIngo Weinhold 116*9980540bSIngo Weinhold return 0; 117*9980540bSIngo Weinhold } 118*9980540bSIngo Weinhold 119*9980540bSIngo Weinhold private: 120*9980540bSIngo Weinhold void _OpenImage() 121*9980540bSIngo Weinhold { 122*9980540bSIngo Weinhold // open the image 123*9980540bSIngo Weinhold fImageFD = open(fImagePath, O_RDWR); 124*9980540bSIngo Weinhold if (fImageFD < 0) { 125*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to open \"%s\": %s.\n", fImagePath, 126*9980540bSIngo Weinhold strerror(errno)); 127*9980540bSIngo Weinhold exit(1); 128*9980540bSIngo Weinhold } 129*9980540bSIngo Weinhold 130*9980540bSIngo Weinhold // get its size 131*9980540bSIngo Weinhold struct stat st; 132*9980540bSIngo Weinhold if (fstat(fImageFD, &st) < 0) { 133*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to stat \"%s\": %s.\n", fImagePath, 134*9980540bSIngo Weinhold strerror(errno)); 135*9980540bSIngo Weinhold exit(1); 136*9980540bSIngo Weinhold } 137*9980540bSIngo Weinhold fImageSize = st.st_size; 138*9980540bSIngo Weinhold } 139*9980540bSIngo Weinhold 140*9980540bSIngo Weinhold void _CreateSocket() 141*9980540bSIngo Weinhold { 142*9980540bSIngo Weinhold // create a socket 143*9980540bSIngo Weinhold fSocket = socket(AF_INET, SOCK_DGRAM, 0); 144*9980540bSIngo Weinhold if (fSocket < 0) { 145*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to create a socket: %s.", 146*9980540bSIngo Weinhold strerror(errno)); 147*9980540bSIngo Weinhold exit(1); 148*9980540bSIngo Weinhold } 149*9980540bSIngo Weinhold 150*9980540bSIngo Weinhold // bind it to the port 151*9980540bSIngo Weinhold sockaddr_in addr; 152*9980540bSIngo Weinhold addr.sin_family = AF_INET; 153*9980540bSIngo Weinhold addr.sin_port = htons(REMOTE_DISK_SERVER_PORT); 154*9980540bSIngo Weinhold addr.sin_addr.s_addr = INADDR_ANY; 155*9980540bSIngo Weinhold if (bind(fSocket, (sockaddr*)&addr, sizeof(addr)) < 0) { 156*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to bind socket to port %hu: %s\n", 157*9980540bSIngo Weinhold REMOTE_DISK_SERVER_PORT, strerror(errno)); 158*9980540bSIngo Weinhold exit(1); 159*9980540bSIngo Weinhold } 160*9980540bSIngo Weinhold } 161*9980540bSIngo Weinhold 162*9980540bSIngo Weinhold void _HandleHelloRequest() 163*9980540bSIngo Weinhold { 164*9980540bSIngo Weinhold printf("HELLO request\n"); 165*9980540bSIngo Weinhold 166*9980540bSIngo Weinhold remote_disk_header reply; 167*9980540bSIngo Weinhold reply.offset = htonll(fImageSize); 168*9980540bSIngo Weinhold 169*9980540bSIngo Weinhold reply.command = REMOTE_DISK_HELLO_REPLY; 170*9980540bSIngo Weinhold _SendReply(&reply, sizeof(remote_disk_header)); 171*9980540bSIngo Weinhold } 172*9980540bSIngo Weinhold 173*9980540bSIngo Weinhold void _HandleReadRequest() 174*9980540bSIngo Weinhold { 175*9980540bSIngo Weinhold char buffer[2048]; 176*9980540bSIngo Weinhold remote_disk_header *reply = (remote_disk_header*)buffer; 177*9980540bSIngo Weinhold uint64_t offset = ntohll(fRequest->offset); 178*9980540bSIngo Weinhold int16_t size = ntohs(fRequest->size); 179*9980540bSIngo Weinhold int16_t result = 0; 180*9980540bSIngo Weinhold 181*9980540bSIngo Weinhold printf("READ request: offset: %llu, %hd bytes\n", offset, size); 182*9980540bSIngo Weinhold 183*9980540bSIngo Weinhold if (offset < (uint64_t)fImageSize && size > 0) { 184*9980540bSIngo Weinhold // always read 1024 bytes 185*9980540bSIngo Weinhold size = REMOTE_DISK_BLOCK_SIZE; 186*9980540bSIngo Weinhold if (offset + size > (uint64_t)fImageSize) 187*9980540bSIngo Weinhold size = fImageSize - offset; 188*9980540bSIngo Weinhold 189*9980540bSIngo Weinhold // seek to the offset 190*9980540bSIngo Weinhold off_t oldOffset = lseek(fImageFD, offset, SEEK_SET); 191*9980540bSIngo Weinhold if (oldOffset >= 0) { 192*9980540bSIngo Weinhold // read 193*9980540bSIngo Weinhold ssize_t bytesRead = read(fImageFD, reply->data, size); 194*9980540bSIngo Weinhold if (bytesRead >= 0) { 195*9980540bSIngo Weinhold result = bytesRead; 196*9980540bSIngo Weinhold } else { 197*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to read at position %llu: " 198*9980540bSIngo Weinhold "%s.", offset, strerror(errno)); 199*9980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR; 200*9980540bSIngo Weinhold } 201*9980540bSIngo Weinhold } else { 202*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to seek to position %llu: %s.", 203*9980540bSIngo Weinhold offset, strerror(errno)); 204*9980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR; 205*9980540bSIngo Weinhold } 206*9980540bSIngo Weinhold } 207*9980540bSIngo Weinhold 208*9980540bSIngo Weinhold // send reply 209*9980540bSIngo Weinhold reply->command = REMOTE_DISK_READ_REPLY; 210*9980540bSIngo Weinhold reply->offset = htonll(offset); 211*9980540bSIngo Weinhold reply->size = htons(result); 212*9980540bSIngo Weinhold _SendReply(reply, sizeof(*reply) + (result >= 0 ? result : 0)); 213*9980540bSIngo Weinhold } 214*9980540bSIngo Weinhold 215*9980540bSIngo Weinhold void _HandleWriteRequest() 216*9980540bSIngo Weinhold { 217*9980540bSIngo Weinhold remote_disk_header reply; 218*9980540bSIngo Weinhold uint64_t offset = ntohll(fRequest->offset); 219*9980540bSIngo Weinhold int16_t size = ntohs(fRequest->size); 220*9980540bSIngo Weinhold int16_t result = 0; 221*9980540bSIngo Weinhold 222*9980540bSIngo Weinhold printf("READ request: offset: %llu, %hd bytes\n", offset, size); 223*9980540bSIngo Weinhold 224*9980540bSIngo Weinhold if (size < 0 225*9980540bSIngo Weinhold || (uint32_t)size > fRequestSize - sizeof(remote_disk_header) 226*9980540bSIngo Weinhold || offset > (uint64_t)fImageSize) { 227*9980540bSIngo Weinhold result = REMOTE_DISK_BAD_REQUEST; 228*9980540bSIngo Weinhold } else if (offset < (uint64_t)fImageSize && size > 0) { 229*9980540bSIngo Weinhold if (offset + size > (uint64_t)fImageSize) 230*9980540bSIngo Weinhold size = fImageSize - offset; 231*9980540bSIngo Weinhold 232*9980540bSIngo Weinhold // seek to the offset 233*9980540bSIngo Weinhold off_t oldOffset = lseek(fImageFD, offset, SEEK_SET); 234*9980540bSIngo Weinhold if (oldOffset >= 0) { 235*9980540bSIngo Weinhold // write 236*9980540bSIngo Weinhold ssize_t bytesWritten = write(fImageFD, fRequest->data, size); 237*9980540bSIngo Weinhold if (bytesWritten >= 0) { 238*9980540bSIngo Weinhold result = bytesWritten; 239*9980540bSIngo Weinhold } else { 240*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to write at position %llu: " 241*9980540bSIngo Weinhold "%s.", offset, strerror(errno)); 242*9980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR; 243*9980540bSIngo Weinhold } 244*9980540bSIngo Weinhold } else { 245*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to seek to position %llu: %s.", 246*9980540bSIngo Weinhold offset, strerror(errno)); 247*9980540bSIngo Weinhold result = REMOTE_DISK_IO_ERROR; 248*9980540bSIngo Weinhold } 249*9980540bSIngo Weinhold } 250*9980540bSIngo Weinhold 251*9980540bSIngo Weinhold // send reply 252*9980540bSIngo Weinhold reply.command = REMOTE_DISK_WRITE_REPLY; 253*9980540bSIngo Weinhold reply.offset = htonll(offset); 254*9980540bSIngo Weinhold reply.size = htons(result); 255*9980540bSIngo Weinhold _SendReply(&reply, sizeof(reply)); 256*9980540bSIngo Weinhold } 257*9980540bSIngo Weinhold 258*9980540bSIngo Weinhold void _SendReply(remote_disk_header *reply, int size) 259*9980540bSIngo Weinhold { 260*9980540bSIngo Weinhold reply->request_id = fRequest->request_id; 261*9980540bSIngo Weinhold reply->port = htons(REMOTE_DISK_SERVER_PORT); 262*9980540bSIngo Weinhold 263*9980540bSIngo Weinhold for (;;) { 264*9980540bSIngo Weinhold ssize_t bytesSent = sendto(fSocket, reply, size, 0, 265*9980540bSIngo Weinhold (const sockaddr*)&fClientAddress, sizeof(fClientAddress)); 266*9980540bSIngo Weinhold 267*9980540bSIngo Weinhold if (bytesSent < 0) { 268*9980540bSIngo Weinhold if (errno == EINTR) 269*9980540bSIngo Weinhold continue; 270*9980540bSIngo Weinhold fprintf(stderr, "Error: Failed to send reply to client: %s.\n", 271*9980540bSIngo Weinhold strerror(errno)); 272*9980540bSIngo Weinhold } 273*9980540bSIngo Weinhold break; 274*9980540bSIngo Weinhold } 275*9980540bSIngo Weinhold } 276*9980540bSIngo Weinhold 277*9980540bSIngo Weinhold private: 278*9980540bSIngo Weinhold const char *fImagePath; 279*9980540bSIngo Weinhold int fImageFD; 280*9980540bSIngo Weinhold off_t fImageSize; 281*9980540bSIngo Weinhold int fSocket; 282*9980540bSIngo Weinhold remote_disk_header *fRequest; 283*9980540bSIngo Weinhold ssize_t fRequestSize; 284*9980540bSIngo Weinhold sockaddr_in fClientAddress; 285*9980540bSIngo Weinhold }; 286*9980540bSIngo Weinhold 287*9980540bSIngo Weinhold 288*9980540bSIngo Weinhold // main 289*9980540bSIngo Weinhold int 290*9980540bSIngo Weinhold main(int argc, const char *const *argv) 291*9980540bSIngo Weinhold { 292*9980540bSIngo Weinhold if (argc != 2) { 293*9980540bSIngo Weinhold fprintf(stderr, "Usage: %s <image path>\n", argv[0]); 294*9980540bSIngo Weinhold exit(1); 295*9980540bSIngo Weinhold } 296*9980540bSIngo Weinhold const char *fileName = argv[1]; 297*9980540bSIngo Weinhold 298*9980540bSIngo Weinhold Server server(fileName); 299*9980540bSIngo Weinhold return server.Run(); 300*9980540bSIngo Weinhold } 301