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