xref: /haiku/src/tools/remote_disk_server/remote_disk_server.cpp (revision 6ce8dc888f27b4670a1cffc2a2bbcfe495468b8b)
19980540bSIngo Weinhold /*
2965b10ccSAxel Dörfler  * Copyright 2005-2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
39980540bSIngo Weinhold  * All rights reserved. Distributed under the terms of the MIT License.
49980540bSIngo Weinhold  */
59980540bSIngo Weinhold 
69980540bSIngo Weinhold #include <endian.h>
79980540bSIngo Weinhold #include <errno.h>
89980540bSIngo Weinhold #include <fcntl.h>
92a39e9e4SIngo Weinhold #include <inttypes.h>
109980540bSIngo Weinhold #include <stdio.h>
119980540bSIngo Weinhold #include <stdlib.h>
129980540bSIngo Weinhold #include <string.h>
139980540bSIngo Weinhold #include <unistd.h>
149980540bSIngo Weinhold #include <netinet/in.h>
159980540bSIngo Weinhold #include <sys/socket.h>
169980540bSIngo Weinhold #include <sys/stat.h>
179980540bSIngo Weinhold 
189980540bSIngo Weinhold #include <boot/net/RemoteDiskDefs.h>
199980540bSIngo Weinhold 
202a39e9e4SIngo Weinhold 
21965b10ccSAxel Dörfler #if defined(__BEOS__) && !defined(__HAIKU__)
22965b10ccSAxel Dörfler typedef int socklen_t;
23965b10ccSAxel Dörfler #endif
24965b10ccSAxel Dörfler 
259980540bSIngo Weinhold 
269980540bSIngo Weinhold #if __BYTE_ORDER == __LITTLE_ENDIAN
279980540bSIngo Weinhold 
289980540bSIngo Weinhold static inline
swap_uint64(uint64_t data)299980540bSIngo Weinhold uint64_t swap_uint64(uint64_t data)
309980540bSIngo Weinhold {
319980540bSIngo Weinhold 	return ((data & 0xff) << 56)
329980540bSIngo Weinhold 		| ((data & 0xff00) << 40)
339980540bSIngo Weinhold 		| ((data & 0xff0000) << 24)
349980540bSIngo Weinhold 		| ((data & 0xff000000) << 8)
359980540bSIngo Weinhold 		| ((data >> 8) & 0xff000000)
369980540bSIngo Weinhold 		| ((data >> 24) & 0xff0000)
379980540bSIngo Weinhold 		| ((data >> 40) & 0xff00)
389980540bSIngo Weinhold 		| ((data >> 56) & 0xff);
399980540bSIngo Weinhold }
409980540bSIngo Weinhold 
419980540bSIngo Weinhold #define host_to_net64(data)	swap_uint64(data)
429980540bSIngo Weinhold #define net_to_host64(data)	swap_uint64(data)
439980540bSIngo Weinhold 
449980540bSIngo Weinhold #endif
459980540bSIngo Weinhold 
469980540bSIngo Weinhold #if __BYTE_ORDER == __BIG_ENDIAN
479980540bSIngo Weinhold #define host_to_net64(data)	(data)
489980540bSIngo Weinhold #define net_to_host64(data)	(data)
499980540bSIngo Weinhold #endif
509980540bSIngo Weinhold 
519980540bSIngo Weinhold #undef htonll
529980540bSIngo Weinhold #undef ntohll
539980540bSIngo Weinhold #define htonll(data)	host_to_net64(data)
549980540bSIngo Weinhold #define ntohll(data)	net_to_host64(data)
559980540bSIngo Weinhold 
569980540bSIngo Weinhold 
579980540bSIngo Weinhold class Server {
589980540bSIngo Weinhold public:
Server(const char * fileName)599980540bSIngo Weinhold 	Server(const char *fileName)
609980540bSIngo Weinhold 		: fImagePath(fileName),
619980540bSIngo Weinhold 		  fImageFD(-1),
629980540bSIngo Weinhold 		  fImageSize(0),
639980540bSIngo Weinhold 		  fSocket(-1)
649980540bSIngo Weinhold 	{
659980540bSIngo Weinhold 	}
669980540bSIngo Weinhold 
Run()679980540bSIngo Weinhold 	int Run()
689980540bSIngo Weinhold 	{
699980540bSIngo Weinhold 		_CreateSocket();
709980540bSIngo Weinhold 
719980540bSIngo Weinhold 		// main server loop
729980540bSIngo Weinhold 		for (;;) {
739980540bSIngo Weinhold 			// receive
749980540bSIngo Weinhold 			fClientAddress.sin_family = AF_INET;
759980540bSIngo Weinhold 			fClientAddress.sin_port = 0;
769980540bSIngo Weinhold 			fClientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
779980540bSIngo Weinhold 			socklen_t addrSize = sizeof(fClientAddress);
789980540bSIngo Weinhold 			char buffer[2048];
799980540bSIngo Weinhold 			ssize_t bytesRead = recvfrom(fSocket, buffer, sizeof(buffer), 0,
809980540bSIngo Weinhold 								(sockaddr*)&fClientAddress, &addrSize);
819980540bSIngo Weinhold 			// handle error
829980540bSIngo Weinhold 			if (bytesRead < 0) {
839980540bSIngo Weinhold 				if (errno == EINTR)
849980540bSIngo Weinhold 					continue;
859980540bSIngo Weinhold 				fprintf(stderr, "Error: Failed to read from socket: %s.\n",
869980540bSIngo Weinhold 					strerror(errno));
879980540bSIngo Weinhold 				exit(1);
889980540bSIngo Weinhold 			}
899980540bSIngo Weinhold 
909980540bSIngo Weinhold 			// short package?
919980540bSIngo Weinhold 			if (bytesRead < (ssize_t)sizeof(remote_disk_header)) {
929980540bSIngo Weinhold 				fprintf(stderr, "Dropping short request package (%d bytes).\n",
932a39e9e4SIngo Weinhold 					(int)bytesRead);
949980540bSIngo Weinhold 				continue;
959980540bSIngo Weinhold 			}
969980540bSIngo Weinhold 
979980540bSIngo Weinhold 			fRequest = (remote_disk_header*)buffer;
989980540bSIngo Weinhold 			fRequestSize = bytesRead;
999980540bSIngo Weinhold 
1009980540bSIngo Weinhold 			switch (fRequest->command) {
1019980540bSIngo Weinhold 				case REMOTE_DISK_HELLO_REQUEST:
1029980540bSIngo Weinhold 					_HandleHelloRequest();
1039980540bSIngo Weinhold 					break;
1049980540bSIngo Weinhold 
1059980540bSIngo Weinhold 				case REMOTE_DISK_READ_REQUEST:
1069980540bSIngo Weinhold 					_HandleReadRequest();
1079980540bSIngo Weinhold 					break;
1089980540bSIngo Weinhold 
1099980540bSIngo Weinhold 				case REMOTE_DISK_WRITE_REQUEST:
1109980540bSIngo Weinhold 					_HandleWriteRequest();
1119980540bSIngo Weinhold 					break;
1129980540bSIngo Weinhold 
1139980540bSIngo Weinhold 				default:
1149980540bSIngo Weinhold 					fprintf(stderr, "Ignoring invalid request %d.\n",
1159980540bSIngo Weinhold 						(int)fRequest->command);
1169980540bSIngo Weinhold 					break;
1179980540bSIngo Weinhold 			}
1189980540bSIngo Weinhold 		}
1199980540bSIngo Weinhold 
1209980540bSIngo Weinhold 		return 0;
1219980540bSIngo Weinhold 	}
1229980540bSIngo Weinhold 
1239980540bSIngo Weinhold private:
_OpenImage(bool reopen)1245615edfdSIngo Weinhold 	void _OpenImage(bool reopen)
1259980540bSIngo Weinhold 	{
1265615edfdSIngo Weinhold 		// already open?
1275615edfdSIngo Weinhold 		if (fImageFD >= 0) {
1285615edfdSIngo Weinhold 			if (!reopen)
1295615edfdSIngo Weinhold 				return;
1305615edfdSIngo Weinhold 
1315615edfdSIngo Weinhold 			close(fImageFD);
1325615edfdSIngo Weinhold 			fImageFD = -1;
1335615edfdSIngo Weinhold 			fImageSize = 0;
1345615edfdSIngo Weinhold 		}
1355615edfdSIngo Weinhold 
1369980540bSIngo Weinhold 		// open the image
1379980540bSIngo Weinhold 		fImageFD = open(fImagePath, O_RDWR);
1389980540bSIngo Weinhold 		if (fImageFD < 0) {
1399980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to open \"%s\": %s.\n", fImagePath,
1409980540bSIngo Weinhold 				strerror(errno));
1419980540bSIngo Weinhold 			exit(1);
1429980540bSIngo Weinhold 		}
1439980540bSIngo Weinhold 
1449980540bSIngo Weinhold 		// get its size
1459980540bSIngo Weinhold 		struct stat st;
1469980540bSIngo Weinhold 		if (fstat(fImageFD, &st) < 0) {
1479980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to stat \"%s\": %s.\n", fImagePath,
1489980540bSIngo Weinhold 				strerror(errno));
1499980540bSIngo Weinhold 			exit(1);
1509980540bSIngo Weinhold 		}
1519980540bSIngo Weinhold 		fImageSize = st.st_size;
1529980540bSIngo Weinhold 	}
1539980540bSIngo Weinhold 
_CreateSocket()1549980540bSIngo Weinhold 	void _CreateSocket()
1559980540bSIngo Weinhold 	{
1569980540bSIngo Weinhold 		// create a socket
1579980540bSIngo Weinhold 		fSocket = socket(AF_INET, SOCK_DGRAM, 0);
1589980540bSIngo Weinhold 		if (fSocket < 0) {
1599980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to create a socket: %s.",
1609980540bSIngo Weinhold 				strerror(errno));
1619980540bSIngo Weinhold 			exit(1);
1629980540bSIngo Weinhold 		}
1639980540bSIngo Weinhold 
1649980540bSIngo Weinhold 		// bind it to the port
1659980540bSIngo Weinhold 		sockaddr_in addr;
1669980540bSIngo Weinhold 		addr.sin_family = AF_INET;
1679980540bSIngo Weinhold 		addr.sin_port = htons(REMOTE_DISK_SERVER_PORT);
1689980540bSIngo Weinhold 		addr.sin_addr.s_addr = INADDR_ANY;
1699980540bSIngo Weinhold 		if (bind(fSocket, (sockaddr*)&addr, sizeof(addr)) < 0) {
1709980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to bind socket to port %hu: %s\n",
1719980540bSIngo Weinhold 				REMOTE_DISK_SERVER_PORT, strerror(errno));
1729980540bSIngo Weinhold 			exit(1);
1739980540bSIngo Weinhold 		}
1749980540bSIngo Weinhold 	}
1759980540bSIngo Weinhold 
_HandleHelloRequest()1769980540bSIngo Weinhold 	void _HandleHelloRequest()
1779980540bSIngo Weinhold 	{
1789980540bSIngo Weinhold 		printf("HELLO request\n");
1799980540bSIngo Weinhold 
1805615edfdSIngo Weinhold 		_OpenImage(true);
1815615edfdSIngo Weinhold 
1829980540bSIngo Weinhold 		remote_disk_header reply;
1839980540bSIngo Weinhold 		reply.offset = htonll(fImageSize);
1849980540bSIngo Weinhold 
1859980540bSIngo Weinhold 		reply.command = REMOTE_DISK_HELLO_REPLY;
1869980540bSIngo Weinhold 		_SendReply(&reply, sizeof(remote_disk_header));
1879980540bSIngo Weinhold 	}
1889980540bSIngo Weinhold 
_HandleReadRequest()1899980540bSIngo Weinhold 	void _HandleReadRequest()
1909980540bSIngo Weinhold 	{
1915615edfdSIngo Weinhold 		_OpenImage(false);
1925615edfdSIngo Weinhold 
1939980540bSIngo Weinhold 		char buffer[2048];
1949980540bSIngo Weinhold 		remote_disk_header *reply = (remote_disk_header*)buffer;
1959980540bSIngo Weinhold 		uint64_t offset = ntohll(fRequest->offset);
1969980540bSIngo Weinhold 		int16_t size = ntohs(fRequest->size);
1979980540bSIngo Weinhold 		int16_t result = 0;
1989980540bSIngo Weinhold 
199*6ce8dc88SAlexander von Gluck IV 		printf("READ request: offset: %" PRIu64 ", %hd bytes\n", offset, size);
2009980540bSIngo Weinhold 
2019980540bSIngo Weinhold 		if (offset < (uint64_t)fImageSize && size > 0) {
2029980540bSIngo Weinhold 			// always read 1024 bytes
2039980540bSIngo Weinhold 			size = REMOTE_DISK_BLOCK_SIZE;
2049980540bSIngo Weinhold 			if (offset + size > (uint64_t)fImageSize)
2059980540bSIngo Weinhold 				size = fImageSize - offset;
2069980540bSIngo Weinhold 
2079980540bSIngo Weinhold 			// seek to the offset
2089980540bSIngo Weinhold 			off_t oldOffset = lseek(fImageFD, offset, SEEK_SET);
2099980540bSIngo Weinhold 			if (oldOffset >= 0) {
2109980540bSIngo Weinhold 				// read
2119980540bSIngo Weinhold 				ssize_t bytesRead = read(fImageFD, reply->data, size);
2129980540bSIngo Weinhold 				if (bytesRead >= 0) {
2139980540bSIngo Weinhold 					result = bytesRead;
2149980540bSIngo Weinhold 				} else {
215*6ce8dc88SAlexander von Gluck IV 					fprintf(stderr, "Error: Failed to read at position %" PRIu64 ": "
2169980540bSIngo Weinhold 						"%s.", offset, strerror(errno));
2179980540bSIngo Weinhold 					result = REMOTE_DISK_IO_ERROR;
2189980540bSIngo Weinhold 				}
2199980540bSIngo Weinhold 			} else {
220*6ce8dc88SAlexander von Gluck IV 				fprintf(stderr, "Error: Failed to seek to position %" PRIu64 ": %s.",
2219980540bSIngo Weinhold 					offset, strerror(errno));
2229980540bSIngo Weinhold 				result = REMOTE_DISK_IO_ERROR;
2239980540bSIngo Weinhold 			}
2249980540bSIngo Weinhold 		}
2259980540bSIngo Weinhold 
2269980540bSIngo Weinhold 		// send reply
2279980540bSIngo Weinhold 		reply->command = REMOTE_DISK_READ_REPLY;
2289980540bSIngo Weinhold 		reply->offset = htonll(offset);
2299980540bSIngo Weinhold 		reply->size = htons(result);
2309980540bSIngo Weinhold 		_SendReply(reply, sizeof(*reply) + (result >= 0 ? result : 0));
2319980540bSIngo Weinhold 	}
2329980540bSIngo Weinhold 
_HandleWriteRequest()2339980540bSIngo Weinhold 	void _HandleWriteRequest()
2349980540bSIngo Weinhold 	{
2355615edfdSIngo Weinhold 		_OpenImage(false);
2365615edfdSIngo Weinhold 
2379980540bSIngo Weinhold 		remote_disk_header reply;
2389980540bSIngo Weinhold 		uint64_t offset = ntohll(fRequest->offset);
2399980540bSIngo Weinhold 		int16_t size = ntohs(fRequest->size);
2409980540bSIngo Weinhold 		int16_t result = 0;
2419980540bSIngo Weinhold 
242*6ce8dc88SAlexander von Gluck IV 		printf("WRITE request: offset: %" PRIu64 ", %hd bytes\n", offset, size);
2439980540bSIngo Weinhold 
2449980540bSIngo Weinhold 		if (size < 0
2459980540bSIngo Weinhold 			|| (uint32_t)size > fRequestSize - sizeof(remote_disk_header)
2469980540bSIngo Weinhold 			|| offset > (uint64_t)fImageSize) {
2479980540bSIngo Weinhold 			result = REMOTE_DISK_BAD_REQUEST;
2489980540bSIngo Weinhold 		} else if (offset < (uint64_t)fImageSize && size > 0) {
2499980540bSIngo Weinhold 			if (offset + size > (uint64_t)fImageSize)
2509980540bSIngo Weinhold 				size = fImageSize - offset;
2519980540bSIngo Weinhold 
2529980540bSIngo Weinhold 			// seek to the offset
2539980540bSIngo Weinhold 			off_t oldOffset = lseek(fImageFD, offset, SEEK_SET);
2549980540bSIngo Weinhold 			if (oldOffset >= 0) {
2559980540bSIngo Weinhold 				// write
2569980540bSIngo Weinhold 				ssize_t bytesWritten = write(fImageFD, fRequest->data, size);
2579980540bSIngo Weinhold 				if (bytesWritten >= 0) {
2589980540bSIngo Weinhold 					result = bytesWritten;
2599980540bSIngo Weinhold 				} else {
260*6ce8dc88SAlexander von Gluck IV 					fprintf(stderr, "Error: Failed to write at position %" PRIu64 ": "
2619980540bSIngo Weinhold 						"%s.", offset, strerror(errno));
2629980540bSIngo Weinhold 					result = REMOTE_DISK_IO_ERROR;
2639980540bSIngo Weinhold 				}
2649980540bSIngo Weinhold 			} else {
265*6ce8dc88SAlexander von Gluck IV 				fprintf(stderr, "Error: Failed to seek to position %" PRIu64 ": %s.",
2669980540bSIngo Weinhold 					offset, strerror(errno));
2679980540bSIngo Weinhold 				result = REMOTE_DISK_IO_ERROR;
2689980540bSIngo Weinhold 			}
2699980540bSIngo Weinhold 		}
2709980540bSIngo Weinhold 
2719980540bSIngo Weinhold 		// send reply
2729980540bSIngo Weinhold 		reply.command = REMOTE_DISK_WRITE_REPLY;
2739980540bSIngo Weinhold 		reply.offset = htonll(offset);
2749980540bSIngo Weinhold 		reply.size = htons(result);
2759980540bSIngo Weinhold 		_SendReply(&reply, sizeof(reply));
2769980540bSIngo Weinhold 	}
2779980540bSIngo Weinhold 
_SendReply(remote_disk_header * reply,int size)2789980540bSIngo Weinhold 	void _SendReply(remote_disk_header *reply, int size)
2799980540bSIngo Weinhold 	{
2809980540bSIngo Weinhold 		reply->request_id = fRequest->request_id;
2819980540bSIngo Weinhold 		reply->port = htons(REMOTE_DISK_SERVER_PORT);
2829980540bSIngo Weinhold 
2839980540bSIngo Weinhold 		for (;;) {
2849980540bSIngo Weinhold 			ssize_t bytesSent = sendto(fSocket, reply, size, 0,
2859980540bSIngo Weinhold 				(const sockaddr*)&fClientAddress, sizeof(fClientAddress));
2869980540bSIngo Weinhold 
2879980540bSIngo Weinhold 			if (bytesSent < 0) {
2889980540bSIngo Weinhold 				if (errno == EINTR)
2899980540bSIngo Weinhold 					continue;
2909980540bSIngo Weinhold 				fprintf(stderr, "Error: Failed to send reply to client: %s.\n",
2919980540bSIngo Weinhold 					strerror(errno));
2929980540bSIngo Weinhold 			}
2939980540bSIngo Weinhold 			break;
2949980540bSIngo Weinhold 		}
2959980540bSIngo Weinhold 	}
2969980540bSIngo Weinhold 
2979980540bSIngo Weinhold private:
2989980540bSIngo Weinhold 	const char			*fImagePath;
2999980540bSIngo Weinhold 	int					fImageFD;
3009980540bSIngo Weinhold 	off_t				fImageSize;
3019980540bSIngo Weinhold 	int					fSocket;
3029980540bSIngo Weinhold 	remote_disk_header	*fRequest;
3039980540bSIngo Weinhold 	ssize_t				fRequestSize;
3049980540bSIngo Weinhold 	sockaddr_in			fClientAddress;
3059980540bSIngo Weinhold };
3069980540bSIngo Weinhold 
3079980540bSIngo Weinhold 
3089980540bSIngo Weinhold // main
3099980540bSIngo Weinhold int
main(int argc,const char * const * argv)3109980540bSIngo Weinhold main(int argc, const char *const *argv)
3119980540bSIngo Weinhold {
3129980540bSIngo Weinhold 	if (argc != 2) {
3139980540bSIngo Weinhold 		fprintf(stderr, "Usage: %s <image path>\n", argv[0]);
3149980540bSIngo Weinhold 		exit(1);
3159980540bSIngo Weinhold 	}
3169980540bSIngo Weinhold 	const char *fileName = argv[1];
3179980540bSIngo Weinhold 
3189980540bSIngo Weinhold 	Server server(fileName);
3199980540bSIngo Weinhold 	return server.Run();
3209980540bSIngo Weinhold }
321