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