1 /* 2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include <boot/net/RemoteDisk.h> 7 8 #include <new> 9 10 #include <endian.h> 11 #include <stdio.h> 12 13 #include <OS.h> 14 #include <SupportDefs.h> 15 16 #include <boot/net/UDP.h> 17 18 19 static const bigtime_t kRequestTimeout = 100000LL; 20 21 22 using std::nothrow; 23 24 25 #if __BYTE_ORDER == __LITTLE_ENDIAN 26 27 static inline 28 uint64_t swap_uint64(uint64_t data) 29 { 30 return ((data & 0xff) << 56) 31 | ((data & 0xff00) << 40) 32 | ((data & 0xff0000) << 24) 33 | ((data & 0xff000000) << 8) 34 | ((data >> 8) & 0xff000000) 35 | ((data >> 24) & 0xff0000) 36 | ((data >> 40) & 0xff00) 37 | ((data >> 56) & 0xff); 38 } 39 40 #define host_to_net64(data) swap_uint64(data) 41 #define net_to_host64(data) swap_uint64(data) 42 43 #endif 44 45 #if __BYTE_ORDER == __BIG_ENDIAN 46 #define host_to_net64(data) (data) 47 #define net_to_host64(data) (data) 48 #endif 49 50 #undef htonll 51 #undef ntohll 52 #define htonll(data) host_to_net64(data) 53 #define ntohll(data) net_to_host64(data) 54 55 56 // constructor 57 RemoteDisk::RemoteDisk() 58 : fServerAddress(INADDR_ANY), 59 fServerPort(0), 60 fImageSize(0), 61 fRequestID(0), 62 fSocket(NULL), 63 fPacket(NULL) 64 { 65 } 66 67 // destructor 68 RemoteDisk::~RemoteDisk() 69 { 70 delete fSocket; 71 delete fPacket; 72 } 73 74 // Init 75 status_t 76 RemoteDisk::Init(ip_addr_t serverAddress, uint16 serverPort, off_t imageSize) 77 { 78 fServerAddress = serverAddress; 79 fServerPort = serverPort; 80 fImageSize = imageSize; 81 82 // create and bind socket 83 fSocket = new(nothrow) UDPSocket; 84 if (!fSocket) 85 return B_NO_MEMORY; 86 87 status_t error = fSocket->Bind(INADDR_ANY, 6666); 88 if (error != B_OK) 89 return error; 90 91 return B_OK; 92 } 93 94 // ReadAt 95 ssize_t 96 RemoteDisk::ReadAt(void */*cookie*/, off_t pos, void *_buffer, 97 size_t bufferSize) 98 { 99 if (!fSocket) 100 return B_NO_INIT; 101 102 uint8 *buffer = (uint8*)_buffer; 103 if (!buffer || pos < 0) 104 return B_BAD_VALUE; 105 106 if (bufferSize == 0) 107 return 0; 108 109 // Check whether the current packet already contains the beginning of the 110 // data to read. 111 ssize_t bytesRead = _ReadFromPacket(pos, buffer, bufferSize); 112 113 // If there still remains something to be read, we need to get it from the 114 // server. 115 status_t error = B_OK; 116 while (bufferSize > 0) { 117 // prepare request 118 remote_disk_header request; 119 request.offset = htonll(pos); 120 uint32 toRead = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE); 121 request.size = htons(toRead); 122 request.command = REMOTE_DISK_READ_REQUEST; 123 124 // send request 125 UDPPacket *packet; 126 error = _SendRequest(&request, sizeof(request), REMOTE_DISK_READ_REPLY, 127 &packet); 128 if (error != B_OK) 129 break; 130 131 // check for errors 132 int16 packetSize = ntohs(((remote_disk_header*)packet->Data())->size); 133 if (packetSize < 0) { 134 if (packetSize == REMOTE_DISK_IO_ERROR) 135 error = B_IO_ERROR; 136 else if (packetSize == REMOTE_DISK_BAD_REQUEST) 137 error = B_BAD_VALUE; 138 break; 139 } 140 141 // make the reply packet the current packet 142 delete fPacket; 143 fPacket = packet; 144 145 // read from the packet 146 size_t packetBytesRead = _ReadFromPacket(pos, buffer, bufferSize); 147 if (packetBytesRead == 0) 148 break; 149 bytesRead += packetBytesRead; 150 } 151 152 // only return an error, when we were not able to read anything at all 153 return (bytesRead == 0 ? error : bytesRead); 154 } 155 156 // WriteAt 157 ssize_t 158 RemoteDisk::WriteAt(void */*cookie*/, off_t pos, const void *buffer, 159 size_t bufferSize) 160 { 161 // Not needed in the boot loader. 162 return B_PERMISSION_DENIED; 163 } 164 165 // GetName 166 status_t 167 RemoteDisk::GetName(char *nameBuffer, size_t bufferSize) const 168 { 169 if (!nameBuffer) 170 return B_BAD_VALUE; 171 172 snprintf(nameBuffer, bufferSize, 173 "RemoteDisk:%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ".%" B_PRIu32 ":%hd", 174 (fServerAddress >> 24) & 0xff, (fServerAddress >> 16) & 0xff, 175 (fServerAddress >> 8) & 0xff, fServerAddress & 0xff, fServerPort); 176 177 return B_OK; 178 } 179 180 // Size 181 off_t 182 RemoteDisk::Size() const 183 { 184 return fImageSize; 185 } 186 187 ip_addr_t 188 RemoteDisk::ServerIPAddress() const 189 { 190 return fServerAddress; 191 } 192 193 uint16 194 RemoteDisk::ServerPort() const 195 { 196 return fServerPort; 197 } 198 199 // FindAnyRemoteDisk 200 RemoteDisk * 201 RemoteDisk::FindAnyRemoteDisk() 202 { 203 // create a socket and bind it 204 UDPSocket socket; 205 status_t error = socket.Bind(INADDR_ANY, 6665); 206 if (error != B_OK) { 207 printf("RemoteDisk::GetAnyRemoteDisk(): Failed to bind socket.\n"); 208 return NULL; 209 } 210 211 // prepare request 212 remote_disk_header request; 213 request.command = REMOTE_DISK_HELLO_REQUEST; 214 215 // send request 216 UDPPacket *packet; 217 error = _SendRequest(&socket, INADDR_BROADCAST, REMOTE_DISK_SERVER_PORT, 218 &request, sizeof(request), REMOTE_DISK_HELLO_REPLY, &packet); 219 if (error != B_OK) { 220 printf("RemoteDisk::GetAnyRemoteDisk(): Got no server reply.\n"); 221 return NULL; 222 } 223 remote_disk_header *reply = (remote_disk_header*)packet->Data(); 224 225 // create a RemoteDisk object 226 RemoteDisk *remoteDisk = new(nothrow) RemoteDisk; 227 if (remoteDisk) { 228 error = remoteDisk->Init(packet->SourceAddress(), ntohs(reply->port), 229 ntohll(reply->offset)); 230 if (error != B_OK) { 231 delete remoteDisk; 232 remoteDisk = NULL; 233 } 234 } 235 236 delete packet; 237 238 return remoteDisk; 239 } 240 241 // _ReadFromPacket 242 ssize_t 243 RemoteDisk::_ReadFromPacket(off_t &pos, uint8 *&buffer, size_t &bufferSize) 244 { 245 if (!fPacket) 246 return 0; 247 248 remote_disk_header *header = (remote_disk_header*)fPacket->Data(); 249 uint64 packetOffset = ntohll(header->offset); 250 uint32 packetSize = ntohs(header->size); 251 if (packetOffset > (uint64)pos || packetOffset + packetSize <= (uint64)pos) 252 return 0; 253 254 // we do indeed have some bytes already 255 size_t toCopy = size_t(packetOffset + packetSize - (uint64)pos); 256 if (toCopy > bufferSize) 257 toCopy = bufferSize; 258 memcpy(buffer, header->data + (pos - packetOffset), toCopy); 259 260 pos += toCopy; 261 buffer += toCopy; 262 bufferSize -= toCopy; 263 return toCopy; 264 } 265 266 // _SendRequest 267 status_t 268 RemoteDisk::_SendRequest(UDPSocket *socket, ip_addr_t serverAddress, 269 uint16 serverPort, remote_disk_header *request, size_t size, 270 uint8 expectedReply, UDPPacket **_packet) 271 { 272 request->port = htons(socket->Port()); 273 274 // try sending the request 3 times at most 275 for (int i = 0; i < 3; i++) { 276 // send request 277 status_t error = socket->Send(serverAddress, serverPort, request, size); 278 if (error != B_OK) 279 return error; 280 281 // receive reply 282 bigtime_t timeout = system_time() + kRequestTimeout; 283 do { 284 UDPPacket *packet; 285 error = socket->Receive(&packet, timeout - system_time()); 286 if (error == B_OK) { 287 // got something; check, if it is looks good 288 if (packet->DataSize() >= sizeof(remote_disk_header)) { 289 remote_disk_header *reply 290 = (remote_disk_header*)packet->Data(); 291 if (reply->request_id == request->request_id 292 && reply->command == expectedReply) { 293 *_packet = packet; 294 return B_OK; 295 } 296 } 297 298 // reply not OK 299 delete packet; 300 } else if (error != B_TIMED_OUT && error != B_WOULD_BLOCK) 301 return error; 302 303 } while (timeout > system_time()); 304 } 305 306 // no reply 307 return B_ERROR; 308 } 309 310 // _SendRequest 311 status_t 312 RemoteDisk::_SendRequest(remote_disk_header *request, size_t size, 313 uint8 expectedReply, UDPPacket **packet) 314 { 315 request->request_id = fRequestID++; 316 return _SendRequest(fSocket, fServerAddress, fServerPort, request, size, 317 expectedReply, packet); 318 } 319