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