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, "RemoteDisk:%ld.%ld.%ld.%ld:%hd", 173 (fServerAddress >> 24) & 0xff, (fServerAddress >> 16) & 0xff, 174 (fServerAddress >> 8) & 0xff, fServerAddress & 0xff, fServerPort); 175 176 return B_OK; 177 } 178 179 // Size 180 off_t 181 RemoteDisk::Size() const 182 { 183 return fImageSize; 184 } 185 186 ip_addr_t 187 RemoteDisk::ServerIPAddress() const 188 { 189 return fServerAddress; 190 } 191 192 uint16 193 RemoteDisk::ServerPort() const 194 { 195 return fServerPort; 196 } 197 198 // FindAnyRemoteDisk 199 RemoteDisk * 200 RemoteDisk::FindAnyRemoteDisk() 201 { 202 // create a socket and bind it 203 UDPSocket socket; 204 status_t error = socket.Bind(INADDR_ANY, 6665); 205 if (error != B_OK) { 206 printf("RemoteDisk::GetAnyRemoteDisk(): Failed to bind socket.\n"); 207 return NULL; 208 } 209 210 // prepare request 211 remote_disk_header request; 212 request.command = REMOTE_DISK_HELLO_REQUEST; 213 214 // send request 215 UDPPacket *packet; 216 error = _SendRequest(&socket, INADDR_BROADCAST, REMOTE_DISK_SERVER_PORT, 217 &request, sizeof(request), REMOTE_DISK_HELLO_REPLY, &packet); 218 if (error != B_OK) { 219 printf("RemoteDisk::GetAnyRemoteDisk(): Got no server reply.\n"); 220 return NULL; 221 } 222 remote_disk_header *reply = (remote_disk_header*)packet->Data(); 223 224 // create a RemoteDisk object 225 RemoteDisk *remoteDisk = new(nothrow) RemoteDisk; 226 if (remoteDisk) { 227 error = remoteDisk->Init(packet->SourceAddress(), ntohs(reply->port), 228 ntohll(reply->offset)); 229 if (error != B_OK) { 230 delete remoteDisk; 231 remoteDisk = NULL; 232 } 233 } 234 235 delete packet; 236 237 return remoteDisk; 238 } 239 240 // _ReadFromPacket 241 ssize_t 242 RemoteDisk::_ReadFromPacket(off_t &pos, uint8 *&buffer, size_t &bufferSize) 243 { 244 if (!fPacket) 245 return 0; 246 247 remote_disk_header *header = (remote_disk_header*)fPacket->Data(); 248 uint64 packetOffset = ntohll(header->offset); 249 uint32 packetSize = ntohs(header->size); 250 if (packetOffset > (uint64)pos || packetOffset + packetSize <= (uint64)pos) 251 return 0; 252 253 // we do indeed have some bytes already 254 size_t toCopy = size_t(packetOffset + packetSize - (uint64)pos); 255 if (toCopy > bufferSize) 256 toCopy = bufferSize; 257 memcpy(buffer, header->data + (pos - packetOffset), toCopy); 258 259 pos += toCopy; 260 buffer += toCopy; 261 bufferSize -= toCopy; 262 return toCopy; 263 } 264 265 // _SendRequest 266 status_t 267 RemoteDisk::_SendRequest(UDPSocket *socket, ip_addr_t serverAddress, 268 uint16 serverPort, remote_disk_header *request, size_t size, 269 uint8 expectedReply, UDPPacket **_packet) 270 { 271 request->port = htons(socket->Port()); 272 273 // try sending the request 3 times at most 274 for (int i = 0; i < 3; i++) { 275 // send request 276 status_t error = socket->Send(serverAddress, serverPort, request, size); 277 if (error != B_OK) 278 return error; 279 280 // receive reply 281 bigtime_t timeout = system_time() + kRequestTimeout; 282 do { 283 UDPPacket *packet; 284 error = socket->Receive(&packet, timeout - system_time()); 285 if (error == B_OK) { 286 // got something; check, if it is looks good 287 if (packet->DataSize() >= sizeof(remote_disk_header)) { 288 remote_disk_header *reply 289 = (remote_disk_header*)packet->Data(); 290 if (reply->request_id == request->request_id 291 && reply->command == expectedReply) { 292 *_packet = packet; 293 return B_OK; 294 } 295 } 296 297 // reply not OK 298 delete packet; 299 } else if (error != B_TIMED_OUT && error != B_WOULD_BLOCK) 300 return error; 301 302 } while (timeout > system_time()); 303 } 304 305 // no reply 306 return B_ERROR; 307 } 308 309 // _SendRequest 310 status_t 311 RemoteDisk::_SendRequest(remote_disk_header *request, size_t size, 312 uint8 expectedReply, UDPPacket **packet) 313 { 314 request->request_id = fRequestID++; 315 return _SendRequest(fSocket, fServerAddress, fServerPort, request, size, 316 expectedReply, packet); 317 } 318