xref: /haiku/src/tools/remote_disk_server/remote_disk_server.cpp (revision 9980540b63b2dcaee6aaf5c2a276280e8e73ede4)
1*9980540bSIngo Weinhold /*
2*9980540bSIngo Weinhold  * Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3*9980540bSIngo Weinhold  * All rights reserved. Distributed under the terms of the MIT License.
4*9980540bSIngo Weinhold  */
5*9980540bSIngo Weinhold 
6*9980540bSIngo Weinhold #include <endian.h>
7*9980540bSIngo Weinhold #include <errno.h>
8*9980540bSIngo Weinhold #include <fcntl.h>
9*9980540bSIngo Weinhold #include <stdio.h>
10*9980540bSIngo Weinhold #include <stdlib.h>
11*9980540bSIngo Weinhold #include <stdint.h>
12*9980540bSIngo Weinhold #include <string.h>
13*9980540bSIngo Weinhold #include <unistd.h>
14*9980540bSIngo Weinhold #include <netinet/in.h>
15*9980540bSIngo Weinhold #include <sys/socket.h>
16*9980540bSIngo Weinhold #include <sys/stat.h>
17*9980540bSIngo Weinhold 
18*9980540bSIngo Weinhold #include <boot/net/RemoteDiskDefs.h>
19*9980540bSIngo Weinhold 
20*9980540bSIngo Weinhold 
21*9980540bSIngo Weinhold #if __BYTE_ORDER == __LITTLE_ENDIAN
22*9980540bSIngo Weinhold 
23*9980540bSIngo Weinhold static inline
24*9980540bSIngo Weinhold uint64_t swap_uint64(uint64_t data)
25*9980540bSIngo Weinhold {
26*9980540bSIngo Weinhold 	return ((data & 0xff) << 56)
27*9980540bSIngo Weinhold 		| ((data & 0xff00) << 40)
28*9980540bSIngo Weinhold 		| ((data & 0xff0000) << 24)
29*9980540bSIngo Weinhold 		| ((data & 0xff000000) << 8)
30*9980540bSIngo Weinhold 		| ((data >> 8) & 0xff000000)
31*9980540bSIngo Weinhold 		| ((data >> 24) & 0xff0000)
32*9980540bSIngo Weinhold 		| ((data >> 40) & 0xff00)
33*9980540bSIngo Weinhold 		| ((data >> 56) & 0xff);
34*9980540bSIngo Weinhold }
35*9980540bSIngo Weinhold 
36*9980540bSIngo Weinhold #define host_to_net64(data)	swap_uint64(data)
37*9980540bSIngo Weinhold #define net_to_host64(data)	swap_uint64(data)
38*9980540bSIngo Weinhold 
39*9980540bSIngo Weinhold #endif
40*9980540bSIngo Weinhold 
41*9980540bSIngo Weinhold #if __BYTE_ORDER == __BIG_ENDIAN
42*9980540bSIngo Weinhold #define host_to_net64(data)	(data)
43*9980540bSIngo Weinhold #define net_to_host64(data)	(data)
44*9980540bSIngo Weinhold #endif
45*9980540bSIngo Weinhold 
46*9980540bSIngo Weinhold #undef htonll
47*9980540bSIngo Weinhold #undef ntohll
48*9980540bSIngo Weinhold #define htonll(data)	host_to_net64(data)
49*9980540bSIngo Weinhold #define ntohll(data)	net_to_host64(data)
50*9980540bSIngo Weinhold 
51*9980540bSIngo Weinhold 
52*9980540bSIngo Weinhold class Server {
53*9980540bSIngo Weinhold public:
54*9980540bSIngo Weinhold 	Server(const char *fileName)
55*9980540bSIngo Weinhold 		: fImagePath(fileName),
56*9980540bSIngo Weinhold 		  fImageFD(-1),
57*9980540bSIngo Weinhold 		  fImageSize(0),
58*9980540bSIngo Weinhold 		  fSocket(-1)
59*9980540bSIngo Weinhold 	{
60*9980540bSIngo Weinhold 	}
61*9980540bSIngo Weinhold 
62*9980540bSIngo Weinhold 	int Run()
63*9980540bSIngo Weinhold 	{
64*9980540bSIngo Weinhold 		_OpenImage();
65*9980540bSIngo Weinhold 		_CreateSocket();
66*9980540bSIngo Weinhold 
67*9980540bSIngo Weinhold 		// main server loop
68*9980540bSIngo Weinhold 		for (;;) {
69*9980540bSIngo Weinhold 			// receive
70*9980540bSIngo Weinhold 			fClientAddress.sin_family = AF_INET;
71*9980540bSIngo Weinhold 			fClientAddress.sin_port = 0;
72*9980540bSIngo Weinhold 			fClientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
73*9980540bSIngo Weinhold 			socklen_t addrSize = sizeof(fClientAddress);
74*9980540bSIngo Weinhold 			char buffer[2048];
75*9980540bSIngo Weinhold 			ssize_t bytesRead = recvfrom(fSocket, buffer, sizeof(buffer), 0,
76*9980540bSIngo Weinhold 								(sockaddr*)&fClientAddress, &addrSize);
77*9980540bSIngo Weinhold 			// handle error
78*9980540bSIngo Weinhold 			if (bytesRead < 0) {
79*9980540bSIngo Weinhold 				if (errno == EINTR)
80*9980540bSIngo Weinhold 					continue;
81*9980540bSIngo Weinhold 				fprintf(stderr, "Error: Failed to read from socket: %s.\n",
82*9980540bSIngo Weinhold 					strerror(errno));
83*9980540bSIngo Weinhold 				exit(1);
84*9980540bSIngo Weinhold 			}
85*9980540bSIngo Weinhold 
86*9980540bSIngo Weinhold 			// short package?
87*9980540bSIngo Weinhold 			if (bytesRead < (ssize_t)sizeof(remote_disk_header)) {
88*9980540bSIngo Weinhold 				fprintf(stderr, "Dropping short request package (%d bytes).\n",
89*9980540bSIngo Weinhold 					bytesRead);
90*9980540bSIngo Weinhold 				continue;
91*9980540bSIngo Weinhold 			}
92*9980540bSIngo Weinhold 
93*9980540bSIngo Weinhold 			fRequest = (remote_disk_header*)buffer;
94*9980540bSIngo Weinhold 			fRequestSize = bytesRead;
95*9980540bSIngo Weinhold 
96*9980540bSIngo Weinhold 			switch (fRequest->command) {
97*9980540bSIngo Weinhold 				case REMOTE_DISK_HELLO_REQUEST:
98*9980540bSIngo Weinhold 					_HandleHelloRequest();
99*9980540bSIngo Weinhold 					break;
100*9980540bSIngo Weinhold 
101*9980540bSIngo Weinhold 				case REMOTE_DISK_READ_REQUEST:
102*9980540bSIngo Weinhold 					_HandleReadRequest();
103*9980540bSIngo Weinhold 					break;
104*9980540bSIngo Weinhold 
105*9980540bSIngo Weinhold 				case REMOTE_DISK_WRITE_REQUEST:
106*9980540bSIngo Weinhold 					_HandleWriteRequest();
107*9980540bSIngo Weinhold 					break;
108*9980540bSIngo Weinhold 
109*9980540bSIngo Weinhold 				default:
110*9980540bSIngo Weinhold 					fprintf(stderr, "Ignoring invalid request %d.\n",
111*9980540bSIngo Weinhold 						(int)fRequest->command);
112*9980540bSIngo Weinhold 					break;
113*9980540bSIngo Weinhold 			}
114*9980540bSIngo Weinhold 		}
115*9980540bSIngo Weinhold 
116*9980540bSIngo Weinhold 		return 0;
117*9980540bSIngo Weinhold 	}
118*9980540bSIngo Weinhold 
119*9980540bSIngo Weinhold private:
120*9980540bSIngo Weinhold 	void _OpenImage()
121*9980540bSIngo Weinhold 	{
122*9980540bSIngo Weinhold 		// open the image
123*9980540bSIngo Weinhold 		fImageFD = open(fImagePath, O_RDWR);
124*9980540bSIngo Weinhold 		if (fImageFD < 0) {
125*9980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to open \"%s\": %s.\n", fImagePath,
126*9980540bSIngo Weinhold 				strerror(errno));
127*9980540bSIngo Weinhold 			exit(1);
128*9980540bSIngo Weinhold 		}
129*9980540bSIngo Weinhold 
130*9980540bSIngo Weinhold 		// get its size
131*9980540bSIngo Weinhold 		struct stat st;
132*9980540bSIngo Weinhold 		if (fstat(fImageFD, &st) < 0) {
133*9980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to stat \"%s\": %s.\n", fImagePath,
134*9980540bSIngo Weinhold 				strerror(errno));
135*9980540bSIngo Weinhold 			exit(1);
136*9980540bSIngo Weinhold 		}
137*9980540bSIngo Weinhold 		fImageSize = st.st_size;
138*9980540bSIngo Weinhold 	}
139*9980540bSIngo Weinhold 
140*9980540bSIngo Weinhold 	void _CreateSocket()
141*9980540bSIngo Weinhold 	{
142*9980540bSIngo Weinhold 		// create a socket
143*9980540bSIngo Weinhold 		fSocket = socket(AF_INET, SOCK_DGRAM, 0);
144*9980540bSIngo Weinhold 		if (fSocket < 0) {
145*9980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to create a socket: %s.",
146*9980540bSIngo Weinhold 				strerror(errno));
147*9980540bSIngo Weinhold 			exit(1);
148*9980540bSIngo Weinhold 		}
149*9980540bSIngo Weinhold 
150*9980540bSIngo Weinhold 		// bind it to the port
151*9980540bSIngo Weinhold 		sockaddr_in addr;
152*9980540bSIngo Weinhold 		addr.sin_family = AF_INET;
153*9980540bSIngo Weinhold 		addr.sin_port = htons(REMOTE_DISK_SERVER_PORT);
154*9980540bSIngo Weinhold 		addr.sin_addr.s_addr = INADDR_ANY;
155*9980540bSIngo Weinhold 		if (bind(fSocket, (sockaddr*)&addr, sizeof(addr)) < 0) {
156*9980540bSIngo Weinhold 			fprintf(stderr, "Error: Failed to bind socket to port %hu: %s\n",
157*9980540bSIngo Weinhold 				REMOTE_DISK_SERVER_PORT, strerror(errno));
158*9980540bSIngo Weinhold 			exit(1);
159*9980540bSIngo Weinhold 		}
160*9980540bSIngo Weinhold 	}
161*9980540bSIngo Weinhold 
162*9980540bSIngo Weinhold 	void _HandleHelloRequest()
163*9980540bSIngo Weinhold 	{
164*9980540bSIngo Weinhold 		printf("HELLO request\n");
165*9980540bSIngo Weinhold 
166*9980540bSIngo Weinhold 		remote_disk_header reply;
167*9980540bSIngo Weinhold 		reply.offset = htonll(fImageSize);
168*9980540bSIngo Weinhold 
169*9980540bSIngo Weinhold 		reply.command = REMOTE_DISK_HELLO_REPLY;
170*9980540bSIngo Weinhold 		_SendReply(&reply, sizeof(remote_disk_header));
171*9980540bSIngo Weinhold 	}
172*9980540bSIngo Weinhold 
173*9980540bSIngo Weinhold 	void _HandleReadRequest()
174*9980540bSIngo Weinhold 	{
175*9980540bSIngo Weinhold 		char buffer[2048];
176*9980540bSIngo Weinhold 		remote_disk_header *reply = (remote_disk_header*)buffer;
177*9980540bSIngo Weinhold 		uint64_t offset = ntohll(fRequest->offset);
178*9980540bSIngo Weinhold 		int16_t size = ntohs(fRequest->size);
179*9980540bSIngo Weinhold 		int16_t result = 0;
180*9980540bSIngo Weinhold 
181*9980540bSIngo Weinhold 		printf("READ request: offset: %llu, %hd bytes\n", offset, size);
182*9980540bSIngo Weinhold 
183*9980540bSIngo Weinhold 		if (offset < (uint64_t)fImageSize && size > 0) {
184*9980540bSIngo Weinhold 			// always read 1024 bytes
185*9980540bSIngo Weinhold 			size = REMOTE_DISK_BLOCK_SIZE;
186*9980540bSIngo Weinhold 			if (offset + size > (uint64_t)fImageSize)
187*9980540bSIngo Weinhold 				size = fImageSize - offset;
188*9980540bSIngo Weinhold 
189*9980540bSIngo Weinhold 			// seek to the offset
190*9980540bSIngo Weinhold 			off_t oldOffset = lseek(fImageFD, offset, SEEK_SET);
191*9980540bSIngo Weinhold 			if (oldOffset >= 0) {
192*9980540bSIngo Weinhold 				// read
193*9980540bSIngo Weinhold 				ssize_t bytesRead = read(fImageFD, reply->data, size);
194*9980540bSIngo Weinhold 				if (bytesRead >= 0) {
195*9980540bSIngo Weinhold 					result = bytesRead;
196*9980540bSIngo Weinhold 				} else {
197*9980540bSIngo Weinhold 					fprintf(stderr, "Error: Failed to read at position %llu: "
198*9980540bSIngo Weinhold 						"%s.", offset, strerror(errno));
199*9980540bSIngo Weinhold 					result = REMOTE_DISK_IO_ERROR;
200*9980540bSIngo Weinhold 				}
201*9980540bSIngo Weinhold 			} else {
202*9980540bSIngo Weinhold 				fprintf(stderr, "Error: Failed to seek to position %llu: %s.",
203*9980540bSIngo Weinhold 					offset, strerror(errno));
204*9980540bSIngo Weinhold 				result = REMOTE_DISK_IO_ERROR;
205*9980540bSIngo Weinhold 			}
206*9980540bSIngo Weinhold 		}
207*9980540bSIngo Weinhold 
208*9980540bSIngo Weinhold 		// send reply
209*9980540bSIngo Weinhold 		reply->command = REMOTE_DISK_READ_REPLY;
210*9980540bSIngo Weinhold 		reply->offset = htonll(offset);
211*9980540bSIngo Weinhold 		reply->size = htons(result);
212*9980540bSIngo Weinhold 		_SendReply(reply, sizeof(*reply) + (result >= 0 ? result : 0));
213*9980540bSIngo Weinhold 	}
214*9980540bSIngo Weinhold 
215*9980540bSIngo Weinhold 	void _HandleWriteRequest()
216*9980540bSIngo Weinhold 	{
217*9980540bSIngo Weinhold 		remote_disk_header reply;
218*9980540bSIngo Weinhold 		uint64_t offset = ntohll(fRequest->offset);
219*9980540bSIngo Weinhold 		int16_t size = ntohs(fRequest->size);
220*9980540bSIngo Weinhold 		int16_t result = 0;
221*9980540bSIngo Weinhold 
222*9980540bSIngo Weinhold 		printf("READ request: offset: %llu, %hd bytes\n", offset, size);
223*9980540bSIngo Weinhold 
224*9980540bSIngo Weinhold 		if (size < 0
225*9980540bSIngo Weinhold 			|| (uint32_t)size > fRequestSize - sizeof(remote_disk_header)
226*9980540bSIngo Weinhold 			|| offset > (uint64_t)fImageSize) {
227*9980540bSIngo Weinhold 			result = REMOTE_DISK_BAD_REQUEST;
228*9980540bSIngo Weinhold 		} else if (offset < (uint64_t)fImageSize && size > 0) {
229*9980540bSIngo Weinhold 			if (offset + size > (uint64_t)fImageSize)
230*9980540bSIngo Weinhold 				size = fImageSize - offset;
231*9980540bSIngo Weinhold 
232*9980540bSIngo Weinhold 			// seek to the offset
233*9980540bSIngo Weinhold 			off_t oldOffset = lseek(fImageFD, offset, SEEK_SET);
234*9980540bSIngo Weinhold 			if (oldOffset >= 0) {
235*9980540bSIngo Weinhold 				// write
236*9980540bSIngo Weinhold 				ssize_t bytesWritten = write(fImageFD, fRequest->data, size);
237*9980540bSIngo Weinhold 				if (bytesWritten >= 0) {
238*9980540bSIngo Weinhold 					result = bytesWritten;
239*9980540bSIngo Weinhold 				} else {
240*9980540bSIngo Weinhold 					fprintf(stderr, "Error: Failed to write at position %llu: "
241*9980540bSIngo Weinhold 						"%s.", offset, strerror(errno));
242*9980540bSIngo Weinhold 					result = REMOTE_DISK_IO_ERROR;
243*9980540bSIngo Weinhold 				}
244*9980540bSIngo Weinhold 			} else {
245*9980540bSIngo Weinhold 				fprintf(stderr, "Error: Failed to seek to position %llu: %s.",
246*9980540bSIngo Weinhold 					offset, strerror(errno));
247*9980540bSIngo Weinhold 				result = REMOTE_DISK_IO_ERROR;
248*9980540bSIngo Weinhold 			}
249*9980540bSIngo Weinhold 		}
250*9980540bSIngo Weinhold 
251*9980540bSIngo Weinhold 		// send reply
252*9980540bSIngo Weinhold 		reply.command = REMOTE_DISK_WRITE_REPLY;
253*9980540bSIngo Weinhold 		reply.offset = htonll(offset);
254*9980540bSIngo Weinhold 		reply.size = htons(result);
255*9980540bSIngo Weinhold 		_SendReply(&reply, sizeof(reply));
256*9980540bSIngo Weinhold 	}
257*9980540bSIngo Weinhold 
258*9980540bSIngo Weinhold 	void _SendReply(remote_disk_header *reply, int size)
259*9980540bSIngo Weinhold 	{
260*9980540bSIngo Weinhold 		reply->request_id = fRequest->request_id;
261*9980540bSIngo Weinhold 		reply->port = htons(REMOTE_DISK_SERVER_PORT);
262*9980540bSIngo Weinhold 
263*9980540bSIngo Weinhold 		for (;;) {
264*9980540bSIngo Weinhold 			ssize_t bytesSent = sendto(fSocket, reply, size, 0,
265*9980540bSIngo Weinhold 				(const sockaddr*)&fClientAddress, sizeof(fClientAddress));
266*9980540bSIngo Weinhold 
267*9980540bSIngo Weinhold 			if (bytesSent < 0) {
268*9980540bSIngo Weinhold 				if (errno == EINTR)
269*9980540bSIngo Weinhold 					continue;
270*9980540bSIngo Weinhold 				fprintf(stderr, "Error: Failed to send reply to client: %s.\n",
271*9980540bSIngo Weinhold 					strerror(errno));
272*9980540bSIngo Weinhold 			}
273*9980540bSIngo Weinhold 			break;
274*9980540bSIngo Weinhold 		}
275*9980540bSIngo Weinhold 	}
276*9980540bSIngo Weinhold 
277*9980540bSIngo Weinhold private:
278*9980540bSIngo Weinhold 	const char			*fImagePath;
279*9980540bSIngo Weinhold 	int					fImageFD;
280*9980540bSIngo Weinhold 	off_t				fImageSize;
281*9980540bSIngo Weinhold 	int					fSocket;
282*9980540bSIngo Weinhold 	remote_disk_header	*fRequest;
283*9980540bSIngo Weinhold 	ssize_t				fRequestSize;
284*9980540bSIngo Weinhold 	sockaddr_in			fClientAddress;
285*9980540bSIngo Weinhold };
286*9980540bSIngo Weinhold 
287*9980540bSIngo Weinhold 
288*9980540bSIngo Weinhold // main
289*9980540bSIngo Weinhold int
290*9980540bSIngo Weinhold main(int argc, const char *const *argv)
291*9980540bSIngo Weinhold {
292*9980540bSIngo Weinhold 	if (argc != 2) {
293*9980540bSIngo Weinhold 		fprintf(stderr, "Usage: %s <image path>\n", argv[0]);
294*9980540bSIngo Weinhold 		exit(1);
295*9980540bSIngo Weinhold 	}
296*9980540bSIngo Weinhold 	const char *fileName = argv[1];
297*9980540bSIngo Weinhold 
298*9980540bSIngo Weinhold 	Server server(fileName);
299*9980540bSIngo Weinhold 	return server.Run();
300*9980540bSIngo Weinhold }
301