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