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 "RemoteDisk.h" 7 8 #include <new> 9 10 #include <endian.h> 11 #include <errno.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/socket.h> 16 #include <sys/time.h> 17 #include <unistd.h> 18 19 #include <KernelExport.h> 20 #include <OS.h> 21 22 #include <kernel.h> // for IS_USER_ADDRESS() 23 24 25 //#define TRACE_REMOTE_DISK 26 #ifdef TRACE_REMOTE_DISK 27 # define TRACE(x) dprintf x 28 #else 29 # define TRACE(x) do {} while (false) 30 #endif 31 32 33 static const bigtime_t kReceiveTimeout = 2000000LL; 34 static const bigtime_t kRequestTimeout = 6000000LL; 35 36 #if __BYTE_ORDER == __LITTLE_ENDIAN 37 38 static inline 39 uint64_t swap_uint64(uint64_t data) 40 { 41 return ((data & 0xff) << 56) 42 | ((data & 0xff00) << 40) 43 | ((data & 0xff0000) << 24) 44 | ((data & 0xff000000) << 8) 45 | ((data >> 8) & 0xff000000) 46 | ((data >> 24) & 0xff0000) 47 | ((data >> 40) & 0xff00) 48 | ((data >> 56) & 0xff); 49 } 50 51 #define host_to_net64(data) swap_uint64(data) 52 #define net_to_host64(data) swap_uint64(data) 53 54 #endif 55 56 #if __BYTE_ORDER == __BIG_ENDIAN 57 #define host_to_net64(data) (data) 58 #define net_to_host64(data) (data) 59 #endif 60 61 #undef htonll 62 #undef ntohll 63 #define htonll(data) host_to_net64(data) 64 #define ntohll(data) net_to_host64(data) 65 66 67 enum { 68 BUFFER_SIZE = 2048 69 }; 70 71 72 // constructor 73 RemoteDisk::RemoteDisk() 74 : fImageSize(0), 75 fRequestID(0), 76 fSocket(-1), 77 fPacket(NULL), 78 fPacketSize(0) 79 { 80 } 81 82 83 // destructor 84 RemoteDisk::~RemoteDisk() 85 { 86 if (fSocket >= 0) 87 close(fSocket); 88 89 free(fPacket); 90 } 91 92 93 // Init 94 status_t 95 RemoteDisk::Init(uint32 serverAddress, uint16 serverPort, off_t imageSize) 96 { 97 status_t error = _Init(); 98 if (error != B_OK) 99 return error; 100 101 fServerAddress.sin_family = AF_INET; 102 fServerAddress.sin_port = htons(serverPort); 103 fServerAddress.sin_addr.s_addr = htonl(serverAddress); 104 fServerAddress.sin_len = sizeof(sockaddr_in); 105 106 fImageSize = imageSize; 107 108 return B_OK; 109 } 110 111 112 // FindAnyRemoteDisk 113 status_t 114 RemoteDisk::FindAnyRemoteDisk() 115 { 116 status_t error = _Init(); 117 if (error != B_OK) 118 return error; 119 120 // prepare request 121 remote_disk_header request; 122 request.command = REMOTE_DISK_HELLO_REQUEST; 123 124 // init server address to broadcast 125 fServerAddress.sin_family = AF_INET; 126 fServerAddress.sin_port = htons(REMOTE_DISK_SERVER_PORT); 127 fServerAddress.sin_addr.s_addr = htonl(INADDR_BROADCAST); 128 fServerAddress.sin_len = sizeof(sockaddr_in); 129 130 // set SO_BROADCAST on socket 131 int soBroadcastValue = 1; 132 if (fSocketModule->setsockopt(fSocket, SOL_SOCKET, SO_BROADCAST, 133 &soBroadcastValue, sizeof(soBroadcastValue)) < 0) { 134 dprintf("RemoteDisk::Init(): Failed to set SO_BROADCAST on socket: " 135 "%s\n", strerror(errno)); 136 } 137 138 // send request 139 sockaddr_in serverAddress; 140 error = _SendRequest(&request, sizeof(request), REMOTE_DISK_HELLO_REPLY, 141 &serverAddress); 142 if (error != B_OK) { 143 dprintf("RemoteDisk::FindAnyRemoteDisk(): Got no server reply: %s\n", 144 strerror(error)); 145 return error; 146 } 147 remote_disk_header* reply = (remote_disk_header*)fPacket; 148 149 // unset SO_BROADCAST on socket 150 soBroadcastValue = 0; 151 if (fSocketModule->setsockopt(fSocket, SOL_SOCKET, SO_BROADCAST, 152 &soBroadcastValue, sizeof(soBroadcastValue)) < 0) { 153 dprintf("RemoteDisk::Init(): Failed to unset SO_BROADCAST on socket: " 154 "%s\n", strerror(errno)); 155 } 156 157 // init server address and size 158 fServerAddress = serverAddress; 159 fServerAddress.sin_port = reply->port; 160 161 fImageSize = ntohll(reply->offset); 162 163 return B_OK; 164 } 165 166 167 // ReadAt 168 ssize_t 169 RemoteDisk::ReadAt(off_t pos, void *_buffer, size_t bufferSize) 170 { 171 if (fSocket < 0) 172 return B_NO_INIT; 173 174 uint8 *buffer = (uint8*)_buffer; 175 if (!buffer || pos < 0) 176 return B_BAD_VALUE; 177 178 if (bufferSize == 0) 179 return 0; 180 181 // Check whether the current packet already contains the beginning of the 182 // data to read. 183 ssize_t bytesRead = _ReadFromPacket(pos, buffer, bufferSize); 184 if (bytesRead < 0) 185 return bytesRead; 186 187 // If there still remains something to be read, we need to get it from the 188 // server. 189 status_t error = B_OK; 190 while (bufferSize > 0) { 191 // prepare request 192 remote_disk_header request; 193 request.offset = htonll(pos); 194 uint32 toRead = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE); 195 request.size = htons(toRead); 196 request.command = REMOTE_DISK_READ_REQUEST; 197 198 // send request 199 error = _SendRequest(&request, sizeof(request), REMOTE_DISK_READ_REPLY); 200 if (error != B_OK) 201 break; 202 203 // check for errors 204 int16 packetSize = ntohs(((remote_disk_header*)fPacket)->size); 205 if (packetSize < 0) { 206 if (packetSize == REMOTE_DISK_IO_ERROR) 207 error = B_IO_ERROR; 208 else if (packetSize == REMOTE_DISK_BAD_REQUEST) 209 error = B_BAD_VALUE; 210 fPacketSize = 0; 211 break; 212 } 213 214 // read from the packet 215 size_t packetBytesRead = _ReadFromPacket(pos, buffer, bufferSize); 216 if (packetBytesRead <= 0) { 217 if (packetBytesRead < 0) 218 error = packetBytesRead; 219 break; 220 } 221 bytesRead += packetBytesRead; 222 } 223 224 // only return an error, when we were not able to read anything at all 225 return (bytesRead == 0 ? error : bytesRead); 226 } 227 228 229 // WriteAt 230 ssize_t 231 RemoteDisk::WriteAt(off_t pos, const void *_buffer, size_t bufferSize) 232 { 233 if (fSocket < 0) 234 return B_NO_INIT; 235 236 const uint8 *buffer = (const uint8*)_buffer; 237 if (!buffer || pos < 0) 238 return B_BAD_VALUE; 239 240 if (bufferSize == 0) 241 return 0; 242 243 status_t error = B_OK; 244 size_t bytesWritten = 0; 245 while (bufferSize > 0) { 246 // prepare request 247 remote_disk_header* request = (remote_disk_header*)fPacket; 248 request->offset = htonll(pos); 249 uint32 toWrite = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE); 250 request->size = htons(toWrite); 251 request->command = REMOTE_DISK_WRITE_REQUEST; 252 253 // copy to packet buffer 254 if (IS_USER_ADDRESS(buffer)) { 255 status_t error = user_memcpy(request->data, buffer, toWrite); 256 if (error != B_OK) 257 return error; 258 } else 259 memcpy(request->data, buffer, toWrite); 260 261 // send request 262 size_t requestSize = request->data + toWrite - (uint8_t*)request; 263 remote_disk_header reply; 264 int32 replySize; 265 error = _SendRequest(request, requestSize, REMOTE_DISK_WRITE_REPLY, 266 NULL, &reply, sizeof(reply), &replySize); 267 if (error != B_OK) 268 break; 269 270 // check for errors 271 int16 packetSize = ntohs(reply.size); 272 if (packetSize < 0) { 273 if (packetSize == REMOTE_DISK_IO_ERROR) 274 error = B_IO_ERROR; 275 else if (packetSize == REMOTE_DISK_BAD_REQUEST) 276 error = B_BAD_VALUE; 277 break; 278 } 279 280 bytesWritten += toWrite; 281 pos += toWrite; 282 buffer += toWrite; 283 bufferSize -= toWrite; 284 } 285 286 // only return an error, when we were not able to write anything at all 287 return (bytesWritten == 0 ? error : bytesWritten); 288 } 289 290 291 // _Init 292 status_t 293 RemoteDisk::_Init() 294 { 295 // get the socket module 296 status_t error = get_module(B_SOCKET_MODULE_NAME, 297 (module_info**)&fSocketModule); 298 if (error != B_OK) { 299 dprintf("RemoteDisk::Init(): Failed to load socket module: %s\n", 300 strerror(error)); 301 return error; 302 } 303 304 // open a control socket for playing with the stack 305 fSocket = fSocketModule->socket(AF_INET, SOCK_DGRAM, 0); 306 if (fSocket < 0) { 307 dprintf("RemoteDisk::Init(): Failed to open socket: %s\n", 308 strerror(errno)); 309 return errno; 310 } 311 312 // bind socket 313 fSocketAddress.sin_family = AF_INET; 314 fSocketAddress.sin_port = 0; 315 fSocketAddress.sin_addr.s_addr = INADDR_ANY; 316 fSocketAddress.sin_len = sizeof(sockaddr_in); 317 if (fSocketModule->bind(fSocket, (sockaddr*)&fSocketAddress, 318 sizeof(fSocketAddress)) < 0) { 319 dprintf("RemoteDisk::Init(): Failed to bind socket: %s\n", 320 strerror(errno)); 321 return errno; 322 } 323 324 // get the port 325 socklen_t addrSize = sizeof(fSocketAddress); 326 if (fSocketModule->getsockname(fSocket, (sockaddr*)&fSocketAddress, 327 &addrSize) < 0) { 328 dprintf("RemoteDisk::Init(): Failed to get socket address: %s\n", 329 strerror(errno)); 330 return errno; 331 } 332 333 // set receive timeout 334 timeval timeout; 335 timeout.tv_sec = time_t(kReceiveTimeout / 1000000LL); 336 timeout.tv_usec = suseconds_t(kReceiveTimeout % 1000000LL); 337 if (fSocketModule->setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, 338 sizeof(timeout)) < 0) { 339 dprintf("RemoteDisk::Init(): Failed to set socket receive timeout: " 340 "%s\n", strerror(errno)); 341 return errno; 342 } 343 344 // allocate buffer 345 fPacket = malloc(BUFFER_SIZE); 346 if (!fPacket) 347 return B_NO_MEMORY; 348 349 return B_OK; 350 } 351 352 353 // _ReadFromPacket 354 ssize_t 355 RemoteDisk::_ReadFromPacket(off_t& pos, uint8*& buffer, size_t& bufferSize) 356 { 357 if (fPacketSize == 0) 358 return 0; 359 360 // check whether the cached packet is indeed a read reply 361 remote_disk_header* header = (remote_disk_header*)fPacket; 362 if (header->command != REMOTE_DISK_READ_REPLY) 363 return 0; 364 365 uint64 packetOffset = ntohll(header->offset); 366 uint32 packetSize = ntohs(header->size); 367 if (packetOffset > (uint64)pos || packetOffset + packetSize <= (uint64)pos) 368 return 0; 369 370 // we have something to copy 371 size_t toCopy = size_t(packetOffset + packetSize - (uint64)pos); 372 if (toCopy > bufferSize) 373 toCopy = bufferSize; 374 375 if (IS_USER_ADDRESS(buffer)) { 376 status_t error = user_memcpy(buffer, 377 header->data + (pos - packetOffset), toCopy); 378 if (error != B_OK) 379 return error; 380 } else 381 memcpy(buffer, header->data + (pos - packetOffset), toCopy); 382 383 pos += toCopy; 384 buffer += toCopy; 385 bufferSize -= toCopy; 386 return toCopy; 387 } 388 389 390 // _SendRequest 391 status_t 392 RemoteDisk::_SendRequest(remote_disk_header* request, size_t size, 393 uint8 expectedReply, sockaddr_in* peerAddress) 394 { 395 return _SendRequest(request, size, expectedReply, peerAddress, fPacket, 396 BUFFER_SIZE, &fPacketSize); 397 } 398 399 400 status_t 401 RemoteDisk::_SendRequest(remote_disk_header *request, size_t size, 402 uint8 expectedReply, sockaddr_in* peerAddress, void* receiveBuffer, 403 size_t receiveBufferSize, int32* _bytesReceived) 404 { 405 request->request_id = fRequestID++; 406 request->port = fSocketAddress.sin_port; 407 408 // try sending the request 3 times at most 409 for (int i = 0; i < 3; i++) { 410 // send request 411 ssize_t bytesSent = fSocketModule->sendto(fSocket, request, size, 412 0, (sockaddr*)&fServerAddress, sizeof(fServerAddress)); 413 if (bytesSent < 0) { 414 dprintf("RemoteDisk::_SendRequest(): failed to send packet: %s\n", 415 strerror(errno)); 416 return errno; 417 } 418 if (bytesSent != (ssize_t)size) { 419 dprintf("RemoteDisk::_SendRequest(): sent less bytes than desired\n"); 420 return B_ERROR; 421 } 422 423 // receive reply 424 bigtime_t timeout = system_time() + kRequestTimeout; 425 do { 426 *_bytesReceived = 0; 427 socklen_t addrSize = sizeof(sockaddr_in); 428 ssize_t bytesReceived = fSocketModule->recvfrom(fSocket, 429 receiveBuffer, receiveBufferSize, 0, (sockaddr*)peerAddress, 430 (peerAddress ? &addrSize : 0)); 431 if (bytesReceived < 0) { 432 status_t error = errno; 433 if (error != B_TIMED_OUT && error != B_WOULD_BLOCK) 434 return error; 435 continue; 436 } 437 438 // got something; check, if it is looks good 439 if (bytesReceived >= (ssize_t)sizeof(remote_disk_header)) { 440 remote_disk_header* reply = (remote_disk_header*)receiveBuffer; 441 if (reply->request_id == request->request_id 442 && reply->command == expectedReply) { 443 *_bytesReceived = bytesReceived; 444 return B_OK; 445 } 446 } 447 } while (timeout > system_time()); 448 } 449 450 // no reply 451 return B_TIMED_OUT; 452 } 453