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