]> git.proxmox.com Git - qemu.git/blobdiff - nbd.c
QMP: Don't use do_info()
[qemu.git] / nbd.c
diff --git a/nbd.c b/nbd.c
index ee5427cab42b92275ec53d380414e56cf2f03ec2..4bf2eb7cb07fafcd72f1e66be50c2a7951aef284 100644 (file)
--- a/nbd.c
+++ b/nbd.c
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include "nbd.h"
 
 #include <errno.h>
 #include <string.h>
+#ifndef _WIN32
 #include <sys/ioctl.h>
+#endif
 #ifdef __sun__
 #include <sys/ioccom.h>
 #endif
 #include <ctype.h>
 #include <inttypes.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#if defined(QEMU_NBD)
-extern int verbose;
-#else
-static int verbose = 0;
-#endif
 
+#include "qemu_socket.h"
+
+//#define DEBUG_NBD
+
+#ifdef DEBUG_NBD
 #define TRACE(msg, ...) do { \
-    if (verbose) LOG(msg, ## __VA_ARGS__); \
+    LOG(msg, ## __VA_ARGS__); \
 } while(0)
+#else
+#define TRACE(msg, ...) \
+    do { } while (0)
+#endif
 
 #define LOG(msg, ...) do { \
     fprintf(stderr, "%s:%s():L%d: " msg "\n", \
@@ -51,6 +49,7 @@ static int verbose = 0;
 
 /* This is all part of the "official" NBD API */
 
+#define NBD_REPLY_SIZE         (4 + 4 + 8)
 #define NBD_REQUEST_MAGIC       0x25609513
 #define NBD_REPLY_MAGIC         0x67446698
 
@@ -64,6 +63,8 @@ static int verbose = 0;
 #define NBD_SET_SIZE_BLOCKS    _IO(0xab, 7)
 #define NBD_DISCONNECT          _IO(0xab, 8)
 
+#define NBD_OPT_EXPORT_NAME    (1 << 0)
+
 /* That's all folks */
 
 #define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true)
@@ -77,11 +78,14 @@ size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read)
         ssize_t len;
 
         if (do_read) {
-            len = read(fd, buffer + offset, size - offset);
+            len = recv(fd, buffer + offset, size - offset, 0);
         } else {
-            len = write(fd, buffer + offset, size - offset);
+            len = send(fd, buffer + offset, size - offset, 0);
         }
 
+        if (len == -1)
+            errno = socket_error();
+
         /* recoverable error */
         if (len == -1 && (errno == EAGAIN || errno == EINTR)) {
             continue;
@@ -108,7 +112,6 @@ int tcp_socket_outgoing(const char *address, uint16_t port)
     int s;
     struct in_addr in;
     struct sockaddr_in addr;
-    int serrno;
 
     s = socket(PF_INET, SOCK_STREAM, 0);
     if (s == -1) {
@@ -136,9 +139,7 @@ int tcp_socket_outgoing(const char *address, uint16_t port)
 
     return s;
 error:
-    serrno = errno;
-    close(s);
-    errno = serrno;
+    closesocket(s);
     return -1;
 }
 
@@ -147,7 +148,6 @@ int tcp_socket_incoming(const char *address, uint16_t port)
     int s;
     struct in_addr in;
     struct sockaddr_in addr;
-    int serrno;
     int opt;
 
     s = socket(PF_INET, SOCK_STREAM, 0);
@@ -171,7 +171,8 @@ int tcp_socket_incoming(const char *address, uint16_t port)
     memcpy(&addr.sin_addr.s_addr, &in, sizeof(in));
 
     opt = 1;
-    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) {
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                   (const void *) &opt, sizeof(opt)) == -1) {
         goto error;
     }
 
@@ -185,17 +186,15 @@ int tcp_socket_incoming(const char *address, uint16_t port)
 
     return s;
 error:
-    serrno = errno;
-    close(s);
-    errno = serrno;
+    closesocket(s);
     return -1;
 }
 
+#ifndef _WIN32
 int unix_socket_incoming(const char *path)
 {
     int s;
     struct sockaddr_un addr;
-    int serrno;
 
     s = socket(PF_UNIX, SOCK_STREAM, 0);
     if (s == -1) {
@@ -216,9 +215,7 @@ int unix_socket_incoming(const char *path)
 
     return s;
 error:
-    serrno = errno;
-    close(s);
-    errno = serrno;
+    closesocket(s);
     return -1;
 }
 
@@ -226,7 +223,6 @@ int unix_socket_outgoing(const char *path)
 {
     int s;
     struct sockaddr_un addr;
-    int serrno;
 
     s = socket(PF_UNIX, SOCK_STREAM, 0);
     if (s == -1) {
@@ -243,11 +239,22 @@ int unix_socket_outgoing(const char *path)
 
     return s;
 error:
-    serrno = errno;
-    close(s);
-    errno = serrno;
+    closesocket(s);
+    return -1;
+}
+#else
+int unix_socket_incoming(const char *path)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+int unix_socket_outgoing(const char *path)
+{
+    errno = ENOTSUP;
     return -1;
 }
+#endif
 
 
 /* Basic flow
@@ -292,34 +299,37 @@ int nbd_negotiate(int csock, off_t size)
        return 0;
 }
 
-int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
+int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags,
+                          off_t *size, size_t *blocksize)
 {
-       char buf[8 + 8 + 8 + 128];
-       uint64_t magic;
+       char buf[256];
+       uint64_t magic, s;
+       uint16_t tmp;
 
        TRACE("Receiving negotation.");
 
-       if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) {
+       if (read_sync(csock, buf, 8) != 8) {
                LOG("read failed");
                errno = EINVAL;
                return -1;
        }
 
-       magic = be64_to_cpup((uint64_t*)(buf + 8));
-       *size = be64_to_cpup((uint64_t*)(buf + 16));
-       *blocksize = 1024;
+       buf[8] = '\0';
+       if (strlen(buf) == 0) {
+               LOG("server connection closed");
+               errno = EINVAL;
+               return -1;
+       }
 
        TRACE("Magic is %c%c%c%c%c%c%c%c",
-             isprint(buf[0]) ? buf[0] : '.',
-             isprint(buf[1]) ? buf[1] : '.',
-             isprint(buf[2]) ? buf[2] : '.',
-             isprint(buf[3]) ? buf[3] : '.',
-             isprint(buf[4]) ? buf[4] : '.',
-             isprint(buf[5]) ? buf[5] : '.',
-             isprint(buf[6]) ? buf[6] : '.',
-             isprint(buf[7]) ? buf[7] : '.');
-       TRACE("Magic is 0x%" PRIx64, magic);
-       TRACE("Size is %" PRIu64, *size);
+             qemu_isprint(buf[0]) ? buf[0] : '.',
+             qemu_isprint(buf[1]) ? buf[1] : '.',
+             qemu_isprint(buf[2]) ? buf[2] : '.',
+             qemu_isprint(buf[3]) ? buf[3] : '.',
+             qemu_isprint(buf[4]) ? buf[4] : '.',
+             qemu_isprint(buf[5]) ? buf[5] : '.',
+             qemu_isprint(buf[6]) ? buf[6] : '.',
+             qemu_isprint(buf[7]) ? buf[7] : '.');
 
        if (memcmp(buf, "NBDMAGIC", 8) != 0) {
                LOG("Invalid magic received");
@@ -327,16 +337,106 @@ int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize)
                return -1;
        }
 
-       TRACE("Checking magic");
+       if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+               LOG("read failed");
+               errno = EINVAL;
+               return -1;
+       }
+       magic = be64_to_cpu(magic);
+       TRACE("Magic is 0x%" PRIx64, magic);
+
+       if (name) {
+               uint32_t reserved = 0;
+               uint32_t opt;
+               uint32_t namesize;
+
+               TRACE("Checking magic (opts_magic)");
+               if (magic != 0x49484156454F5054LL) {
+                       LOG("Bad magic received");
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+                       LOG("flags read failed");
+                       errno = EINVAL;
+                       return -1;
+               }
+               *flags = be16_to_cpu(tmp) << 16;
+               /* reserved for future use */
+               if (write_sync(csock, &reserved, sizeof(reserved)) !=
+                   sizeof(reserved)) {
+                       LOG("write failed (reserved)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               /* write the export name */
+               magic = cpu_to_be64(magic);
+               if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) {
+                       LOG("write failed (magic)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
+               if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) {
+                       LOG("write failed (opt)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               namesize = cpu_to_be32(strlen(name));
+               if (write_sync(csock, &namesize, sizeof(namesize)) !=
+                   sizeof(namesize)) {
+                       LOG("write failed (namesize)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) {
+                       LOG("write failed (name)");
+                       errno = EINVAL;
+                       return -1;
+               }
+       } else {
+               TRACE("Checking magic (cli_magic)");
+
+               if (magic != 0x00420281861253LL) {
+                       LOG("Bad magic received");
+                       errno = EINVAL;
+                       return -1;
+               }
+       }
+
+       if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) {
+               LOG("read failed");
+               errno = EINVAL;
+               return -1;
+       }
+       *size = be64_to_cpu(s);
+       *blocksize = 1024;
+       TRACE("Size is %" PRIu64, *size);
 
-       if (magic != 0x00420281861253LL) {
-               LOG("Bad magic received");
+       if (!name) {
+               if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) {
+                       LOG("read failed (flags)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               *flags = be32_to_cpup(flags);
+       } else {
+               if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+                       LOG("read failed (tmp)");
+                       errno = EINVAL;
+                       return -1;
+               }
+               *flags |= be32_to_cpu(tmp);
+       }
+       if (read_sync(csock, &buf, 124) != 124) {
+               LOG("read failed (buf)");
                errno = EINVAL;
                return -1;
        }
         return 0;
 }
 
+#ifndef _WIN32
 int nbd_init(int fd, int csock, off_t size, size_t blocksize)
 {
        TRACE("Setting block size to %lu", (unsigned long)blocksize);
@@ -348,8 +448,7 @@ int nbd_init(int fd, int csock, off_t size, size_t blocksize)
                return -1;
        }
 
-       TRACE("Setting size to %llu block(s)",
-             (unsigned long long)(size / blocksize));
+        TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize));
 
        if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) {
                int serrno = errno;
@@ -389,7 +488,7 @@ int nbd_disconnect(int fd)
        return 0;
 }
 
-int nbd_client(int fd, int csock)
+int nbd_client(int fd)
 {
        int ret;
        int serrno;
@@ -410,6 +509,25 @@ int nbd_client(int fd, int csock)
        errno = serrno;
        return ret;
 }
+#else
+int nbd_init(int fd, int csock, off_t size, size_t blocksize)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+int nbd_disconnect(int fd)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+
+int nbd_client(int fd)
+{
+    errno = ENOTSUP;
+    return -1;
+}
+#endif
 
 int nbd_send_request(int csock, struct nbd_request *request)
 {
@@ -471,7 +589,7 @@ static int nbd_receive_request(int csock, struct nbd_request *request)
 
 int nbd_receive_reply(int csock, struct nbd_reply *reply)
 {
-       uint8_t buf[4 + 4 + 8];
+       uint8_t buf[NBD_REPLY_SIZE];
        uint32_t magic;
 
        memset(buf, 0xAA, sizeof(buf));
@@ -538,9 +656,9 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
        if (nbd_receive_request(csock, &request) == -1)
                return -1;
 
-       if (request.len > data_size) {
+       if (request.len + NBD_REPLY_SIZE > data_size) {
                LOG("len (%u) is larger than max len (%u)",
-                   request.len, data_size);
+                   request.len + NBD_REPLY_SIZE, data_size);
                errno = EINVAL;
                return -1;
        }
@@ -555,7 +673,7 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
        if ((request.from + request.len) > size) {
                LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64
                    ", Offset: %" PRIu64 "\n",
-                    request.from, request.len, size, dev_offset);
+                    request.from, request.len, (uint64_t)size, dev_offset);
                LOG("requested operation past EOF--bad client?");
                errno = EINVAL;
                return -1;
@@ -570,7 +688,8 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
        case NBD_CMD_READ:
                TRACE("Request type is READ");
 
-               if (bdrv_read(bs, (request.from + dev_offset) / 512, data,
+               if (bdrv_read(bs, (request.from + dev_offset) / 512,
+                             data + NBD_REPLY_SIZE,
                              request.len / 512) == -1) {
                        LOG("reading from file failed");
                        errno = EINVAL;
@@ -580,12 +699,21 @@ int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset,
 
                TRACE("Read %u byte(s)", request.len);
 
-               if (nbd_send_reply(csock, &reply) == -1)
-                       return -1;
+               /* Reply
+                  [ 0 ..  3]    magic   (NBD_REPLY_MAGIC)
+                  [ 4 ..  7]    error   (0 == no error)
+                  [ 7 .. 15]    handle
+                */
+
+               cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC);
+               cpu_to_be32w((uint32_t*)(data + 4), reply.error);
+               cpu_to_be64w((uint64_t*)(data + 8), reply.handle);
 
                TRACE("Sending data to client");
 
-               if (write_sync(csock, data, request.len) != request.len) {
+               if (write_sync(csock, data,
+                              request.len + NBD_REPLY_SIZE) !=
+                              request.len + NBD_REPLY_SIZE) {
                        LOG("writing to socket failed");
                        errno = EINVAL;
                        return -1;