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