blockdev-nbd.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * Serving QEMU block devices via NBD
  3. *
  4. * Copyright (c) 2012 Red Hat, Inc.
  5. *
  6. * Author: Paolo Bonzini <pbonzini@redhat.com>
  7. *
  8. * This work is licensed under the terms of the GNU GPL, version 2 or
  9. * later. See the COPYING file in the top-level directory.
  10. */
  11. #include "qemu/osdep.h"
  12. #include "sysemu/blockdev.h"
  13. #include "sysemu/block-backend.h"
  14. #include "hw/block/block.h"
  15. #include "qapi/qmp/qerror.h"
  16. #include "sysemu/sysemu.h"
  17. #include "qmp-commands.h"
  18. #include "block/nbd.h"
  19. #include "io/channel-socket.h"
  20. typedef struct NBDServerData {
  21. QIOChannelSocket *listen_ioc;
  22. int watch;
  23. QCryptoTLSCreds *tlscreds;
  24. } NBDServerData;
  25. static NBDServerData *nbd_server;
  26. static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
  27. {
  28. nbd_client_put(client);
  29. }
  30. static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
  31. gpointer opaque)
  32. {
  33. QIOChannelSocket *cioc;
  34. if (!nbd_server) {
  35. return FALSE;
  36. }
  37. cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
  38. NULL);
  39. if (!cioc) {
  40. return TRUE;
  41. }
  42. qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
  43. nbd_client_new(NULL, cioc,
  44. nbd_server->tlscreds, NULL,
  45. nbd_blockdev_client_closed);
  46. object_unref(OBJECT(cioc));
  47. return TRUE;
  48. }
  49. static void nbd_server_free(NBDServerData *server)
  50. {
  51. if (!server) {
  52. return;
  53. }
  54. if (server->watch != -1) {
  55. g_source_remove(server->watch);
  56. }
  57. object_unref(OBJECT(server->listen_ioc));
  58. if (server->tlscreds) {
  59. object_unref(OBJECT(server->tlscreds));
  60. }
  61. g_free(server);
  62. }
  63. static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
  64. {
  65. Object *obj;
  66. QCryptoTLSCreds *creds;
  67. obj = object_resolve_path_component(
  68. object_get_objects_root(), id);
  69. if (!obj) {
  70. error_setg(errp, "No TLS credentials with id '%s'",
  71. id);
  72. return NULL;
  73. }
  74. creds = (QCryptoTLSCreds *)
  75. object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS);
  76. if (!creds) {
  77. error_setg(errp, "Object with id '%s' is not TLS credentials",
  78. id);
  79. return NULL;
  80. }
  81. if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) {
  82. error_setg(errp,
  83. "Expecting TLS credentials with a server endpoint");
  84. return NULL;
  85. }
  86. object_ref(obj);
  87. return creds;
  88. }
  89. void nbd_server_start(SocketAddress *addr, const char *tls_creds,
  90. Error **errp)
  91. {
  92. if (nbd_server) {
  93. error_setg(errp, "NBD server already running");
  94. return;
  95. }
  96. nbd_server = g_new0(NBDServerData, 1);
  97. nbd_server->watch = -1;
  98. nbd_server->listen_ioc = qio_channel_socket_new();
  99. qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc),
  100. "nbd-listener");
  101. if (qio_channel_socket_listen_sync(
  102. nbd_server->listen_ioc, addr, errp) < 0) {
  103. goto error;
  104. }
  105. if (tls_creds) {
  106. nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp);
  107. if (!nbd_server->tlscreds) {
  108. goto error;
  109. }
  110. /* TODO SOCKET_ADDRESS_TYPE_FD where fd has AF_INET or AF_INET6 */
  111. if (addr->type != SOCKET_ADDRESS_TYPE_INET) {
  112. error_setg(errp, "TLS is only supported with IPv4/IPv6");
  113. goto error;
  114. }
  115. }
  116. nbd_server->watch = qio_channel_add_watch(
  117. QIO_CHANNEL(nbd_server->listen_ioc),
  118. G_IO_IN,
  119. nbd_accept,
  120. NULL,
  121. NULL);
  122. return;
  123. error:
  124. nbd_server_free(nbd_server);
  125. nbd_server = NULL;
  126. }
  127. void qmp_nbd_server_start(SocketAddressLegacy *addr,
  128. bool has_tls_creds, const char *tls_creds,
  129. Error **errp)
  130. {
  131. SocketAddress *addr_flat = socket_address_flatten(addr);
  132. nbd_server_start(addr_flat, tls_creds, errp);
  133. qapi_free_SocketAddress(addr_flat);
  134. }
  135. void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
  136. Error **errp)
  137. {
  138. BlockDriverState *bs = NULL;
  139. BlockBackend *on_eject_blk;
  140. NBDExport *exp;
  141. if (!nbd_server) {
  142. error_setg(errp, "NBD server not running");
  143. return;
  144. }
  145. if (nbd_export_find(device)) {
  146. error_setg(errp, "NBD server already exporting device '%s'", device);
  147. return;
  148. }
  149. on_eject_blk = blk_by_name(device);
  150. bs = bdrv_lookup_bs(device, device, errp);
  151. if (!bs) {
  152. return;
  153. }
  154. if (!has_writable) {
  155. writable = false;
  156. }
  157. if (bdrv_is_read_only(bs)) {
  158. writable = false;
  159. }
  160. exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
  161. NULL, false, on_eject_blk, errp);
  162. if (!exp) {
  163. return;
  164. }
  165. nbd_export_set_name(exp, device);
  166. /* The list of named exports has a strong reference to this export now and
  167. * our only way of accessing it is through nbd_export_find(), so we can drop
  168. * the strong reference that is @exp. */
  169. nbd_export_put(exp);
  170. }
  171. void qmp_nbd_server_stop(Error **errp)
  172. {
  173. nbd_export_close_all();
  174. nbd_server_free(nbd_server);
  175. nbd_server = NULL;
  176. }