headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / boot / loader / net / RemoteDisk.cpp
blob4cce254694c9710d4089327b1eb4769f9e5f70cd
1 /*
2 * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
6 #include <boot/net/RemoteDisk.h>
8 #include <new>
10 #include <endian.h>
11 #include <stdio.h>
13 #include <OS.h>
14 #include <SupportDefs.h>
16 #include <boot/net/UDP.h>
19 static const bigtime_t kRequestTimeout = 100000LL;
22 using std::nothrow;
25 #if __BYTE_ORDER == __LITTLE_ENDIAN
27 static inline
28 uint64_t swap_uint64(uint64_t data)
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);
40 #define host_to_net64(data) swap_uint64(data)
41 #define net_to_host64(data) swap_uint64(data)
43 #endif
45 #if __BYTE_ORDER == __BIG_ENDIAN
46 #define host_to_net64(data) (data)
47 #define net_to_host64(data) (data)
48 #endif
50 #undef htonll
51 #undef ntohll
52 #define htonll(data) host_to_net64(data)
53 #define ntohll(data) net_to_host64(data)
56 // constructor
57 RemoteDisk::RemoteDisk()
58 : fServerAddress(INADDR_ANY),
59 fServerPort(0),
60 fImageSize(0),
61 fRequestID(0),
62 fSocket(NULL),
63 fPacket(NULL)
67 // destructor
68 RemoteDisk::~RemoteDisk()
70 delete fSocket;
71 delete fPacket;
74 // Init
75 status_t
76 RemoteDisk::Init(ip_addr_t serverAddress, uint16 serverPort, off_t imageSize)
78 fServerAddress = serverAddress;
79 fServerPort = serverPort;
80 fImageSize = imageSize;
82 // create and bind socket
83 fSocket = new(nothrow) UDPSocket;
84 if (!fSocket)
85 return B_NO_MEMORY;
87 status_t error = fSocket->Bind(INADDR_ANY, 6666);
88 if (error != B_OK)
89 return error;
91 return B_OK;
94 // ReadAt
95 ssize_t
96 RemoteDisk::ReadAt(void */*cookie*/, off_t pos, void *_buffer,
97 size_t bufferSize)
99 if (!fSocket)
100 return B_NO_INIT;
102 uint8 *buffer = (uint8*)_buffer;
103 if (!buffer || pos < 0)
104 return B_BAD_VALUE;
106 if (bufferSize == 0)
107 return 0;
109 // Check whether the current packet already contains the beginning of the
110 // data to read.
111 ssize_t bytesRead = _ReadFromPacket(pos, buffer, bufferSize);
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;
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;
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;
141 // make the reply packet the current packet
142 delete fPacket;
143 fPacket = packet;
145 // read from the packet
146 size_t packetBytesRead = _ReadFromPacket(pos, buffer, bufferSize);
147 if (packetBytesRead == 0)
148 break;
149 bytesRead += packetBytesRead;
152 // only return an error, when we were not able to read anything at all
153 return (bytesRead == 0 ? error : bytesRead);
156 // WriteAt
157 ssize_t
158 RemoteDisk::WriteAt(void */*cookie*/, off_t pos, const void *buffer,
159 size_t bufferSize)
161 // Not needed in the boot loader.
162 return B_PERMISSION_DENIED;
165 // GetName
166 status_t
167 RemoteDisk::GetName(char *nameBuffer, size_t bufferSize) const
169 if (!nameBuffer)
170 return B_BAD_VALUE;
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);
177 return B_OK;
180 // Size
181 off_t
182 RemoteDisk::Size() const
184 return fImageSize;
187 ip_addr_t
188 RemoteDisk::ServerIPAddress() const
190 return fServerAddress;
193 uint16
194 RemoteDisk::ServerPort() const
196 return fServerPort;
199 // FindAnyRemoteDisk
200 RemoteDisk *
201 RemoteDisk::FindAnyRemoteDisk()
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;
211 // prepare request
212 remote_disk_header request;
213 request.command = REMOTE_DISK_HELLO_REQUEST;
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;
223 remote_disk_header *reply = (remote_disk_header*)packet->Data();
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;
236 delete packet;
238 return remoteDisk;
241 // _ReadFromPacket
242 ssize_t
243 RemoteDisk::_ReadFromPacket(off_t &pos, uint8 *&buffer, size_t &bufferSize)
245 if (!fPacket)
246 return 0;
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;
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);
260 pos += toCopy;
261 buffer += toCopy;
262 bufferSize -= toCopy;
263 return toCopy;
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)
272 request->port = htons(socket->Port());
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;
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;
298 // reply not OK
299 delete packet;
300 } else if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
301 return error;
303 } while (timeout > system_time());
306 // no reply
307 return B_ERROR;
310 // _SendRequest
311 status_t
312 RemoteDisk::_SendRequest(remote_disk_header *request, size_t size,
313 uint8 expectedReply, UDPPacket **packet)
315 request->request_id = fRequestID++;
316 return _SendRequest(fSocket, fServerAddress, fServerPort, request, size,
317 expectedReply, packet);