nbd-client.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Copyright 2010 Rob Landley <rob@landley.net>
  3. *
  4. * Licensed under GPLv2, see file LICENSE in this source tree.
  5. */
  6. //config:config NBDCLIENT
  7. //config: bool "nbd-client (6 kb)"
  8. //config: default y
  9. //config: help
  10. //config: Network block device client
  11. //applet:IF_NBDCLIENT(APPLET_NOEXEC(nbd-client, nbdclient, BB_DIR_USR_SBIN, BB_SUID_DROP, nbdclient))
  12. //kbuild:lib-$(CONFIG_NBDCLIENT) += nbd-client.o
  13. #include "libbb.h"
  14. #include <netinet/tcp.h>
  15. #include <linux/fs.h>
  16. #include <getopt.h>
  17. #define NBD_SET_SOCK _IO(0xab, 0)
  18. #define NBD_SET_BLKSIZE _IO(0xab, 1)
  19. #define NBD_SET_SIZE _IO(0xab, 2)
  20. #define NBD_DO_IT _IO(0xab, 3)
  21. #define NBD_CLEAR_SOCK _IO(0xab, 4)
  22. #define NBD_CLEAR_QUEUE _IO(0xab, 5)
  23. #define NBD_PRINT_DEBUG _IO(0xab, 6)
  24. #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7)
  25. #define NBD_DISCONNECT _IO(0xab, 8)
  26. #define NBD_SET_TIMEOUT _IO(0xab, 9)
  27. #define NBD_SET_FLAGS _IO(0xab, 10)
  28. //usage:#define nbdclient_trivial_usage
  29. //usage: "{ [-b BLKSIZE] [-N NAME] [-t SEC] [-p] HOST [PORT] | -d } BLOCKDEV"
  30. //usage:#define nbdclient_full_usage "\n\n"
  31. //usage: "Connect to HOST and provide network block device on BLOCKDEV"
  32. //TODO: more compat with nbd-client version 3.17 -
  33. //nbd-client host [ port ] nbd-device [ -connections num ] [ -sdp ] [ -swap ]
  34. // [ -persist ] [ -nofork ] [ -nonetlink ] [ -systemd-mark ]
  35. // [ -block-size block size ] [ -timeout seconds ] [ -name name ]
  36. // [ -certfile certfile ] [ -keyfile keyfile ] [ -cacertfile cacertfile ]
  37. // [ -tlshostname hostname ]
  38. //nbd-client -unix path nbd-device [ -connections num ] [ -sdp ] [ -swap ]
  39. // [ -persist ] [ -nofork ] [ -nonetlink ] [ -systemd-mark ]
  40. // [ -block-size block size ] [ -timeout seconds ] [ -name name ]
  41. //nbd-client nbd-device
  42. //nbd-client -d nbd-device
  43. //nbd-client -c nbd-device
  44. //nbd-client -l host [ port ]
  45. //nbd-client [ -netlink ] -l host
  46. //
  47. //Default value for blocksize is 4096
  48. //Allowed values for blocksize are 512,1024,2048,4096
  49. int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  50. int nbdclient_main(int argc, char **argv)
  51. {
  52. #if BB_MMU
  53. bool nofork;
  54. #endif
  55. bool opt_d;
  56. bool opt_p;
  57. const char *host, *port, *device;
  58. const char *name;
  59. unsigned blksize, size_blocks;
  60. unsigned timeout;
  61. int ch;
  62. struct nbd_header_t {
  63. uint64_t magic1; // "NBDMAGIC"
  64. uint64_t magic2; // old style: 0x420281861253 big endian
  65. // // new style: 0x49484156454F5054 (IHAVEOPT)
  66. } nbd_header;
  67. struct old_nbd_header_t {
  68. uint64_t devsize;
  69. uint32_t flags;
  70. char data[124];
  71. } old_nbd_header;
  72. struct new_nbd_header_t {
  73. uint64_t devsize;
  74. uint16_t transmission_flags;
  75. char data[124];
  76. } new_nbd_header;
  77. struct nbd_opt_t {
  78. uint64_t magic;
  79. uint32_t opt;
  80. uint32_t len;
  81. } nbd_opts;
  82. static const struct option long_options[] = {
  83. { "block-size", required_argument, NULL, 'b' },
  84. { "timeout" , required_argument, NULL, 't' },
  85. { "name" , required_argument, NULL, 'n' },
  86. { "persist" , no_argument , NULL, 'p' },
  87. { NULL }
  88. };
  89. BUILD_BUG_ON(offsetof(struct old_nbd_header_t, data) != 8 + 4);
  90. BUILD_BUG_ON(offsetof(struct new_nbd_header_t, data) != 8 + 2);
  91. #if !BB_MMU
  92. bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
  93. #endif
  94. // Parse args. nbd-client uses stupid "one-dash long options" style :(
  95. // Even though short forms (-b,-t,-N,-p) exist for all long opts,
  96. // older manpages only contained long forms, which probably resulted
  97. // in many scripts using them.
  98. blksize = 4096;
  99. timeout = 0;
  100. name = ""; // use of "" instead of NULL simplifies strlen() later
  101. opt_d = opt_p = 0;
  102. while ((ch = getopt_long_only(argc, argv, "dN:", long_options, NULL)) != -1) {
  103. switch (ch) {
  104. case 'p': // -persist
  105. opt_p = 1;
  106. break;
  107. case 'd': // -d
  108. opt_d = 1;
  109. break;
  110. case 'b': // -block-size
  111. blksize = xatou(optarg);
  112. break;
  113. case 't': // -timeout
  114. timeout = xatou(optarg);
  115. break;
  116. case 'N': // -N
  117. case 'n': // -name
  118. name = optarg;
  119. break;
  120. default:
  121. bb_show_usage();
  122. }
  123. }
  124. argv += optind;
  125. if (opt_d) { // -d
  126. if (argv[0] && !argv[1]) {
  127. int nbd = xopen(argv[0], O_RDWR);
  128. ioctl(nbd, NBD_DISCONNECT);
  129. ioctl(nbd, NBD_CLEAR_SOCK);
  130. if (ENABLE_FEATURE_CLEAN_UP)
  131. close(nbd);
  132. return 0;
  133. }
  134. bb_show_usage();
  135. }
  136. // Allow only argv[] of: HOST [PORT] BLOCKDEV
  137. if (!argv[0] || !argv[1] || (argv[2] && argv[3])) {
  138. bb_show_usage();
  139. }
  140. host = argv[0];
  141. port = argv[2] ? argv[1] : "10809";
  142. device = argv[2] ? argv[2] : argv[1];
  143. // Repeat until spanked if -persist
  144. #if BB_MMU
  145. nofork = 0;
  146. #endif
  147. do {
  148. int sock, nbd;
  149. int ro;
  150. int proto_new; // 0 for old, 1 for new
  151. #if BB_MMU
  152. char *data;
  153. #endif
  154. // Make sure BLOCKDEV exists
  155. nbd = xopen(device, O_RDWR);
  156. // Find and connect to server
  157. sock = create_and_connect_stream_or_die(host, xatou16(port));
  158. setsockopt_1(sock, IPPROTO_TCP, TCP_NODELAY);
  159. // Log on to the server
  160. xread(sock, &nbd_header, 8 + 8);
  161. if (memcmp(&nbd_header.magic1, "NBDMAGIC",
  162. sizeof(nbd_header.magic1)) != 0
  163. ) {
  164. bb_error_msg_and_die("login failed");
  165. }
  166. if (memcmp(&nbd_header.magic2,
  167. "\x00\x00\x42\x02\x81\x86\x12\x53",
  168. sizeof(nbd_header.magic2)) == 0
  169. ) {
  170. proto_new = 0;
  171. } else if (memcmp(&nbd_header.magic2, "IHAVEOPT", 8) == 0) {
  172. proto_new = 1;
  173. } else {
  174. bb_error_msg_and_die("login failed");
  175. }
  176. if (!proto_new) {
  177. xread(sock, &old_nbd_header,
  178. sizeof(old_nbd_header.devsize) +
  179. sizeof(old_nbd_header.flags) +
  180. sizeof(old_nbd_header.data));
  181. size_blocks = SWAP_BE64(old_nbd_header.devsize) / blksize;
  182. ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long) blksize);
  183. ioctl(nbd, NBD_SET_SIZE_BLOCKS, size_blocks);
  184. ioctl(nbd, NBD_CLEAR_SOCK);
  185. ro = !!(old_nbd_header.flags & htons(2));
  186. #if BB_MMU
  187. data = old_nbd_header.data;
  188. #endif
  189. } else {
  190. unsigned namelen;
  191. uint16_t handshake_flags;
  192. xread(sock, &handshake_flags, sizeof(handshake_flags));
  193. xwrite(sock, &const_int_0, sizeof(const_int_0)); // client_flags
  194. memcpy(&nbd_opts.magic, "IHAVEOPT",
  195. sizeof(nbd_opts.magic));
  196. nbd_opts.opt = htonl(1); // NBD_OPT_EXPORT_NAME
  197. namelen = strlen(name);
  198. nbd_opts.len = htonl(namelen);
  199. xwrite(sock, &nbd_opts,
  200. sizeof(nbd_opts.magic) +
  201. sizeof(nbd_opts.opt) +
  202. sizeof(nbd_opts.len));
  203. xwrite(sock, name, namelen);
  204. xread(sock, &new_nbd_header,
  205. sizeof(new_nbd_header.devsize) +
  206. sizeof(new_nbd_header.transmission_flags) +
  207. sizeof(new_nbd_header.data));
  208. size_blocks = SWAP_BE64(new_nbd_header.devsize) / blksize;
  209. ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long) blksize);
  210. ioctl(nbd, NBD_SET_SIZE_BLOCKS, size_blocks);
  211. ioctl(nbd, NBD_CLEAR_SOCK);
  212. ioctl(nbd, NBD_SET_FLAGS,
  213. ntohs(new_nbd_header.transmission_flags));
  214. ro = !!(new_nbd_header.transmission_flags & htons(2));
  215. #if BB_MMU
  216. data = new_nbd_header.data;
  217. #endif
  218. }
  219. if (ioctl(nbd, BLKROSET, &ro) < 0) {
  220. bb_perror_msg_and_die("BLKROSET");
  221. }
  222. if (timeout) {
  223. if (ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long) timeout)) {
  224. bb_perror_msg_and_die("NBD_SET_TIMEOUT");
  225. }
  226. }
  227. if (ioctl(nbd, NBD_SET_SOCK, sock)) {
  228. bb_perror_msg_and_die("NBD_SET_SOCK");
  229. }
  230. //if (swap) mlockall(MCL_CURRENT|MCL_FUTURE);
  231. #if BB_MMU
  232. // Open the device to force reread of the partition table.
  233. // Need to do it in a separate process, since open(device)
  234. // needs some other process to sit in ioctl(nbd, NBD_DO_IT).
  235. if (fork() == 0) {
  236. /* child */
  237. char *s = strrchr(device, '/');
  238. sprintf(data, "/sys/block/%.32s/pid", s ? s + 1 : device);
  239. // Is it up yet?
  240. for (;;) {
  241. int fd = open(data, O_RDONLY);
  242. if (fd >= 0) {
  243. if (ENABLE_FEATURE_CLEAN_UP)
  244. close(fd);
  245. break;
  246. }
  247. sleep(1);
  248. }
  249. open(device, O_RDONLY);
  250. return 0;
  251. }
  252. // Daemonize here
  253. if (!nofork) {
  254. daemon(0, 0);
  255. nofork = 1;
  256. }
  257. #endif
  258. // This turns us (the process that calls this ioctl)
  259. // into a dedicated NBD request handler.
  260. // We block here for a long time.
  261. // When exactly ioctl returns? On a signal,
  262. // or if someone does ioctl(NBD_DISCONNECT) [nbd-client -d].
  263. if (ioctl(nbd, NBD_DO_IT) >= 0 || errno == EBADR) {
  264. // Flush queue and exit
  265. ioctl(nbd, NBD_CLEAR_QUEUE);
  266. ioctl(nbd, NBD_CLEAR_SOCK);
  267. break;
  268. }
  269. close(sock);
  270. close(nbd);
  271. } while (opt_p);
  272. return 0;
  273. }