xref: /haiku/src/add-ons/kernel/drivers/disk/virtual/remote_disk/RemoteDisk.cpp (revision 2fd04ff545da7e33c82c06b502c48b725287f81f)
1 /*
2  * Copyright 2005-2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include "RemoteDisk.h"
7 
8 #include <new>
9 
10 #include <endian.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/socket.h>
16 #include <sys/time.h>
17 #include <unistd.h>
18 
19 #include <KernelExport.h>
20 #include <OS.h>
21 
22 #include <kernel.h>		// for IS_USER_ADDRESS()
23 
24 
25 //#define TRACE_REMOTE_DISK
26 #ifdef TRACE_REMOTE_DISK
27 #	define TRACE(x) dprintf x
28 #else
29 #	define TRACE(x) do {} while (false)
30 #endif
31 
32 
33 static const int kMaxRequestResendCount = 5;
34 static const bigtime_t kReceiveTimeout = 800000LL;
35 static const bigtime_t kRequestTimeout = 5000000LL;
36 
37 #if __BYTE_ORDER == __LITTLE_ENDIAN
38 
39 static inline
swap_uint64(uint64_t data)40 uint64_t swap_uint64(uint64_t data)
41 {
42 	return ((data & 0xff) << 56)
43 		| ((data & 0xff00) << 40)
44 		| ((data & 0xff0000) << 24)
45 		| ((data & 0xff000000) << 8)
46 		| ((data >> 8) & 0xff000000)
47 		| ((data >> 24) & 0xff0000)
48 		| ((data >> 40) & 0xff00)
49 		| ((data >> 56) & 0xff);
50 }
51 
52 #define host_to_net64(data)	swap_uint64(data)
53 #define net_to_host64(data)	swap_uint64(data)
54 
55 #endif
56 
57 #if __BYTE_ORDER == __BIG_ENDIAN
58 #define host_to_net64(data)	(data)
59 #define net_to_host64(data)	(data)
60 #endif
61 
62 #undef htonll
63 #undef ntohll
64 #define htonll(data)	host_to_net64(data)
65 #define ntohll(data)	net_to_host64(data)
66 
67 
68 enum {
69 	BUFFER_SIZE	= 2048
70 };
71 
72 
73 // constructor
RemoteDisk()74 RemoteDisk::RemoteDisk()
75 	: fImageSize(0),
76 	  fRequestID(0),
77 	  fSocket(-1),
78 	  fPacket(NULL),
79 	  fPacketSize(0)
80 {
81 }
82 
83 
84 // destructor
~RemoteDisk()85 RemoteDisk::~RemoteDisk()
86 {
87 	if (fSocket >= 0)
88 		close(fSocket);
89 
90 	free(fPacket);
91 }
92 
93 
94 // Init
95 status_t
Init(uint32 serverAddress,uint16 serverPort,off_t imageSize)96 RemoteDisk::Init(uint32 serverAddress, uint16 serverPort, off_t imageSize)
97 {
98 	status_t error = _Init();
99 	if (error != B_OK)
100 		return error;
101 
102 	fServerAddress.sin_family = AF_INET;
103 	fServerAddress.sin_port = htons(serverPort);
104 	fServerAddress.sin_addr.s_addr = htonl(serverAddress);
105 	fServerAddress.sin_len = sizeof(sockaddr_in);
106 
107 	fImageSize = imageSize;
108 
109 	return B_OK;
110 }
111 
112 
113 // FindAnyRemoteDisk
114 status_t
FindAnyRemoteDisk()115 RemoteDisk::FindAnyRemoteDisk()
116 {
117 	status_t error = _Init();
118 	if (error != B_OK)
119 		return error;
120 
121 	// prepare request
122 	remote_disk_header request;
123 	request.command = REMOTE_DISK_HELLO_REQUEST;
124 
125 	// init server address to broadcast
126 	fServerAddress.sin_family = AF_INET;
127 	fServerAddress.sin_port = htons(REMOTE_DISK_SERVER_PORT);
128 	fServerAddress.sin_addr.s_addr = htonl(INADDR_BROADCAST);
129 	fServerAddress.sin_len = sizeof(sockaddr_in);
130 
131 	// set SO_BROADCAST on socket
132 	int soBroadcastValue = 1;
133 	if (setsockopt(fSocket, SOL_SOCKET, SO_BROADCAST, &soBroadcastValue,
134 			sizeof(soBroadcastValue)) < 0) {
135 		dprintf("RemoteDisk::Init(): Failed to set SO_BROADCAST on socket: "
136 			"%s\n", strerror(errno));
137 	}
138 
139 	// send request
140 	sockaddr_in serverAddress;
141 	error = _SendRequest(&request, sizeof(request), REMOTE_DISK_HELLO_REPLY,
142 		&serverAddress);
143 	if (error != B_OK) {
144 		dprintf("RemoteDisk::FindAnyRemoteDisk(): Got no server reply: %s\n",
145 			strerror(error));
146 		return error;
147 	}
148 	remote_disk_header* reply = (remote_disk_header*)fPacket;
149 
150 	// unset SO_BROADCAST on socket
151 	soBroadcastValue = 0;
152 	if (setsockopt(fSocket, SOL_SOCKET, SO_BROADCAST, &soBroadcastValue,
153 			sizeof(soBroadcastValue)) < 0) {
154 		dprintf("RemoteDisk::Init(): Failed to unset SO_BROADCAST on socket: "
155 			"%s\n", strerror(errno));
156 	}
157 
158 	// init server address and size
159 	fServerAddress = serverAddress;
160 	fServerAddress.sin_port = reply->port;
161 
162 	fImageSize = ntohll(reply->offset);
163 
164 	return B_OK;
165 }
166 
167 
168 // ReadAt
169 ssize_t
ReadAt(off_t pos,void * _buffer,size_t bufferSize)170 RemoteDisk::ReadAt(off_t pos, void *_buffer, size_t bufferSize)
171 {
172 	if (fSocket < 0)
173 		return B_NO_INIT;
174 
175 	uint8 *buffer = (uint8*)_buffer;
176 	if (!buffer || pos < 0)
177 		return B_BAD_VALUE;
178 
179 	if (bufferSize == 0)
180 		return 0;
181 
182 	// Check whether the current packet already contains the beginning of the
183 	// data to read.
184 	ssize_t bytesRead = _ReadFromPacket(pos, buffer, bufferSize);
185 	if (bytesRead < 0)
186 		return bytesRead;
187 
188 	// If there still remains something to be read, we need to get it from the
189 	// server.
190 	status_t error = B_OK;
191 	while (bufferSize > 0) {
192 		// prepare request
193 		remote_disk_header request;
194 		request.offset = htonll(pos);
195 		uint32 toRead = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE);
196 		request.size = htons(toRead);
197 		request.command = REMOTE_DISK_READ_REQUEST;
198 
199 		// send request
200 		error = _SendRequest(&request, sizeof(request), REMOTE_DISK_READ_REPLY);
201 		if (error != B_OK)
202 			break;
203 
204 		// check for errors
205 		int16 packetSize = ntohs(((remote_disk_header*)fPacket)->size);
206 		if (packetSize < 0) {
207 			if (packetSize == REMOTE_DISK_IO_ERROR)
208 				error = B_IO_ERROR;
209 			else if (packetSize == REMOTE_DISK_BAD_REQUEST)
210 				error = B_BAD_VALUE;
211 			fPacketSize = 0;
212 			break;
213 		}
214 
215 		// read from the packet
216 		size_t packetBytesRead = _ReadFromPacket(pos, buffer, bufferSize);
217 		if (packetBytesRead <= 0) {
218 			if (packetBytesRead < 0)
219 				error = packetBytesRead;
220 			break;
221 		}
222 		bytesRead += packetBytesRead;
223 	}
224 
225 	// only return an error, when we were not able to read anything at all
226 	return (bytesRead == 0 ? error : bytesRead);
227 }
228 
229 
230 // WriteAt
231 ssize_t
WriteAt(off_t pos,const void * _buffer,size_t bufferSize)232 RemoteDisk::WriteAt(off_t pos, const void *_buffer, size_t bufferSize)
233 {
234 	if (fSocket < 0)
235 		return B_NO_INIT;
236 
237 	const uint8 *buffer = (const uint8*)_buffer;
238 	if (!buffer || pos < 0)
239 		return B_BAD_VALUE;
240 
241 	if (bufferSize == 0)
242 		return 0;
243 
244 	status_t error = B_OK;
245 	size_t bytesWritten = 0;
246 	while (bufferSize > 0) {
247 		// prepare request
248 		remote_disk_header* request = (remote_disk_header*)fPacket;
249 		request->offset = htonll(pos);
250 		uint32 toWrite = min_c(bufferSize, REMOTE_DISK_BLOCK_SIZE);
251 		request->size = htons(toWrite);
252 		request->command = REMOTE_DISK_WRITE_REQUEST;
253 
254 		// copy to packet buffer
255 		if (IS_USER_ADDRESS(buffer)) {
256 			status_t error = user_memcpy(request->data, buffer, toWrite);
257 			if (error != B_OK)
258 				return error;
259 		} else
260 			memcpy(request->data, buffer, toWrite);
261 
262 		// send request
263 		size_t requestSize = request->data + toWrite - (uint8_t*)request;
264 		remote_disk_header reply;
265 		int32 replySize;
266 		error = _SendRequest(request, requestSize, REMOTE_DISK_WRITE_REPLY,
267 			NULL, &reply, sizeof(reply), &replySize);
268 		if (error != B_OK)
269 			break;
270 
271 		// check for errors
272 		int16 packetSize = ntohs(reply.size);
273 		if (packetSize < 0) {
274 			if (packetSize == REMOTE_DISK_IO_ERROR)
275 				error = B_IO_ERROR;
276 			else if (packetSize == REMOTE_DISK_BAD_REQUEST)
277 				error = B_BAD_VALUE;
278 			break;
279 		}
280 
281 		bytesWritten += toWrite;
282 		pos += toWrite;
283 		buffer += toWrite;
284 		bufferSize -= toWrite;
285 	}
286 
287 	// only return an error, when we were not able to write anything at all
288 	return (bytesWritten == 0 ? error : bytesWritten);
289 }
290 
291 
292 // _Init
293 status_t
_Init()294 RemoteDisk::_Init()
295 {
296 	// open a control socket for playing with the stack
297 	fSocket = socket(AF_INET, SOCK_DGRAM, 0);
298 	if (fSocket < 0) {
299 		dprintf("RemoteDisk::Init(): Failed to open socket: %s\n",
300 			strerror(errno));
301 		return errno;
302 	}
303 
304 	// bind socket
305 	fSocketAddress.sin_family = AF_INET;
306 	fSocketAddress.sin_port = 0;
307 	fSocketAddress.sin_addr.s_addr = INADDR_ANY;
308 	fSocketAddress.sin_len = sizeof(sockaddr_in);
309 	if (bind(fSocket, (sockaddr*)&fSocketAddress, sizeof(fSocketAddress)) < 0) {
310 		dprintf("RemoteDisk::Init(): Failed to bind socket: %s\n",
311 			strerror(errno));
312 		return errno;
313 	}
314 
315    // get the port
316     socklen_t addrSize = sizeof(fSocketAddress);
317     if (getsockname(fSocket, (sockaddr*)&fSocketAddress, &addrSize) < 0) {
318 		dprintf("RemoteDisk::Init(): Failed to get socket address: %s\n",
319 			strerror(errno));
320         return errno;
321     }
322 
323 	// set receive timeout
324 	timeval timeout;
325 	timeout.tv_sec = time_t(kReceiveTimeout / 1000000LL);
326 	timeout.tv_usec = suseconds_t(kReceiveTimeout % 1000000LL);
327 	if (setsockopt(fSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))
328 			< 0) {
329 		dprintf("RemoteDisk::Init(): Failed to set socket receive timeout: "
330 			"%s\n", strerror(errno));
331         return errno;
332 	}
333 
334 	// allocate buffer
335 	fPacket = malloc(BUFFER_SIZE);
336 	if (!fPacket)
337 		return B_NO_MEMORY;
338 
339 	return B_OK;
340 }
341 
342 
343 // _ReadFromPacket
344 ssize_t
_ReadFromPacket(off_t & pos,uint8 * & buffer,size_t & bufferSize)345 RemoteDisk::_ReadFromPacket(off_t& pos, uint8*& buffer, size_t& bufferSize)
346 {
347 	if (fPacketSize == 0)
348 		return 0;
349 
350 	// check whether the cached packet is indeed a read reply
351 	remote_disk_header* header = (remote_disk_header*)fPacket;
352 	if (header->command != REMOTE_DISK_READ_REPLY)
353 		return 0;
354 
355 	uint64 packetOffset = ntohll(header->offset);
356 	uint32 packetSize = ntohs(header->size);
357 	if (packetOffset > (uint64)pos || packetOffset + packetSize <= (uint64)pos)
358 		return 0;
359 
360 	// we have something to copy
361 	size_t toCopy = size_t(packetOffset + packetSize - (uint64)pos);
362 	if (toCopy > bufferSize)
363 		toCopy = bufferSize;
364 
365 	if (IS_USER_ADDRESS(buffer)) {
366 		status_t error = user_memcpy(buffer,
367 			header->data + (pos - packetOffset), toCopy);
368 		if (error != B_OK)
369 			return error;
370 	} else
371 		memcpy(buffer, header->data + (pos - packetOffset), toCopy);
372 
373 	pos += toCopy;
374 	buffer += toCopy;
375 	bufferSize -= toCopy;
376 	return toCopy;
377 }
378 
379 
380 // _SendRequest
381 status_t
_SendRequest(remote_disk_header * request,size_t size,uint8 expectedReply,sockaddr_in * peerAddress)382 RemoteDisk::_SendRequest(remote_disk_header* request, size_t size,
383 	uint8 expectedReply, sockaddr_in* peerAddress)
384 {
385 	return _SendRequest(request, size, expectedReply, peerAddress, fPacket,
386 		BUFFER_SIZE, &fPacketSize);
387 }
388 
389 
390 status_t
_SendRequest(remote_disk_header * request,size_t size,uint8 expectedReply,sockaddr_in * peerAddress,void * receiveBuffer,size_t receiveBufferSize,int32 * _bytesReceived)391 RemoteDisk::_SendRequest(remote_disk_header *request, size_t size,
392 	uint8 expectedReply, sockaddr_in* peerAddress, void* receiveBuffer,
393 	size_t receiveBufferSize, int32* _bytesReceived)
394 {
395 	request->request_id = fRequestID++;
396 	request->port = fSocketAddress.sin_port;
397 
398 	// try sending the request kMaxRequestResendCount times at most
399 	for (int i = 0; i < kMaxRequestResendCount; i++) {
400 		// send request
401 		ssize_t bytesSent;
402 		do {
403 			bytesSent = sendto(fSocket, request, size, 0,
404 				(sockaddr*)&fServerAddress, sizeof(fServerAddress));
405 		} while (bytesSent < 0 && errno == B_INTERRUPTED);
406 
407 		if (bytesSent < 0) {
408 			dprintf("RemoteDisk::_SendRequest(): failed to send packet: %s\n",
409 				strerror(errno));
410 			return errno;
411 		}
412 		if (bytesSent != (ssize_t)size) {
413 			dprintf("RemoteDisk::_SendRequest(): sent less bytes than "
414 				"desired\n");
415 			return B_ERROR;
416 		}
417 
418 		// receive reply
419 		bigtime_t timeout = system_time() + kRequestTimeout;
420 		do {
421 			*_bytesReceived = 0;
422 			socklen_t addrSize = sizeof(sockaddr_in);
423 			ssize_t bytesReceived = recvfrom(fSocket, receiveBuffer,
424 				receiveBufferSize, 0, (sockaddr*)peerAddress,
425 				(peerAddress ? &addrSize : NULL));
426 			if (bytesReceived < 0) {
427 				status_t error = errno;
428 				if (error != B_TIMED_OUT && error != B_WOULD_BLOCK
429 						&& error != B_INTERRUPTED) {
430 					dprintf("RemoteDisk::_SendRequest(): recvfrom() failed: "
431 						"%s\n", strerror(error));
432 					return error;
433 				}
434 				continue;
435 			}
436 
437 			// got something; check, if it is looks good
438 			if (bytesReceived >= (ssize_t)sizeof(remote_disk_header)) {
439 				remote_disk_header* reply = (remote_disk_header*)receiveBuffer;
440 				if (reply->request_id == request->request_id
441 					&& reply->command == expectedReply) {
442 					*_bytesReceived = bytesReceived;
443 					return B_OK;
444 				}
445 			}
446 		} while (timeout > system_time());
447 	}
448 
449 	// no reply
450 	return B_TIMED_OUT;
451 }
452