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