PageRenderTime 26ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/src/tools/remote_disk_server/remote_disk_server.cpp

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