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