mirror of
https://github.com/qemu/qemu.git
synced 2025-08-09 10:25:06 +00:00
nbd: Share common option-sending code in client
Rather than open-coding each option request, it's easier to have common helper functions do the work. That in turn requires having convenient packed types for handling option requests and replies. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1476469998-28592-9-git-send-email-eblake@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
3668328303
commit
c8a3a1b6c4
@ -26,15 +26,34 @@
|
|||||||
#include "io/channel-socket.h"
|
#include "io/channel-socket.h"
|
||||||
#include "crypto/tlscreds.h"
|
#include "crypto/tlscreds.h"
|
||||||
|
|
||||||
/* Note: these are _NOT_ the same as the network representation of an NBD
|
/* Handshake phase structs - this struct is passed on the wire */
|
||||||
|
|
||||||
|
struct nbd_option {
|
||||||
|
uint64_t magic; /* NBD_OPTS_MAGIC */
|
||||||
|
uint32_t option; /* NBD_OPT_* */
|
||||||
|
uint32_t length;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct nbd_option nbd_option;
|
||||||
|
|
||||||
|
struct nbd_opt_reply {
|
||||||
|
uint64_t magic; /* NBD_REP_MAGIC */
|
||||||
|
uint32_t option; /* NBD_OPT_* */
|
||||||
|
uint32_t type; /* NBD_REP_* */
|
||||||
|
uint32_t length;
|
||||||
|
} QEMU_PACKED;
|
||||||
|
typedef struct nbd_opt_reply nbd_opt_reply;
|
||||||
|
|
||||||
|
/* Transmission phase structs
|
||||||
|
*
|
||||||
|
* Note: these are _NOT_ the same as the network representation of an NBD
|
||||||
* request and reply!
|
* request and reply!
|
||||||
*/
|
*/
|
||||||
struct NBDRequest {
|
struct NBDRequest {
|
||||||
uint64_t handle;
|
uint64_t handle;
|
||||||
uint64_t from;
|
uint64_t from;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
uint16_t flags;
|
uint16_t flags; /* NBD_CMD_FLAG_* */
|
||||||
uint16_t type;
|
uint16_t type; /* NBD_CMD_* */
|
||||||
};
|
};
|
||||||
typedef struct NBDRequest NBDRequest;
|
typedef struct NBDRequest NBDRequest;
|
||||||
|
|
||||||
|
259
nbd/client.c
259
nbd/client.c
@ -75,64 +75,128 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports);
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Send an option request.
|
||||||
/* If type represents success, return 1 without further action.
|
*
|
||||||
* If type represents an error reply, consume the rest of the packet on ioc.
|
* The request is for option @opt, with @data containing @len bytes of
|
||||||
* Then return 0 for unsupported (so the client can fall back to
|
* additional payload for the request (@len may be -1 to treat @data as
|
||||||
* other approaches), or -1 with errp set for other errors.
|
* a C string; and @data may be NULL if @len is 0).
|
||||||
*/
|
* Return 0 if successful, -1 with errp set if it is impossible to
|
||||||
static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,
|
* continue. */
|
||||||
|
static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt,
|
||||||
|
uint32_t len, const char *data,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
nbd_option req;
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(req) != 16);
|
||||||
|
|
||||||
|
if (len == -1) {
|
||||||
|
req.length = len = strlen(data);
|
||||||
|
}
|
||||||
|
TRACE("Sending option request %" PRIu32", len %" PRIu32, opt, len);
|
||||||
|
|
||||||
|
stq_be_p(&req.magic, NBD_OPTS_MAGIC);
|
||||||
|
stl_be_p(&req.option, opt);
|
||||||
|
stl_be_p(&req.length, len);
|
||||||
|
|
||||||
|
if (write_sync(ioc, &req, sizeof(req)) != sizeof(req)) {
|
||||||
|
error_setg(errp, "Failed to send option request header");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len && write_sync(ioc, (char *) data, len) != len) {
|
||||||
|
error_setg(errp, "Failed to send option request data");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive the header of an option reply, which should match the given
|
||||||
|
* opt. Read through the length field, but NOT the length bytes of
|
||||||
|
* payload. Return 0 if successful, -1 with errp set if it is
|
||||||
|
* impossible to continue. */
|
||||||
|
static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt,
|
||||||
|
nbd_opt_reply *reply, Error **errp)
|
||||||
|
{
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(*reply) != 20);
|
||||||
|
if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) {
|
||||||
|
error_setg(errp, "failed to read option reply");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
be64_to_cpus(&reply->magic);
|
||||||
|
be32_to_cpus(&reply->option);
|
||||||
|
be32_to_cpus(&reply->type);
|
||||||
|
be32_to_cpus(&reply->length);
|
||||||
|
|
||||||
|
TRACE("Received option reply %" PRIx32", type %" PRIx32", len %" PRIu32,
|
||||||
|
reply->option, reply->type, reply->length);
|
||||||
|
|
||||||
|
if (reply->magic != NBD_REP_MAGIC) {
|
||||||
|
error_setg(errp, "Unexpected option reply magic");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (reply->option != opt) {
|
||||||
|
error_setg(errp, "Unexpected option type %x expected %x",
|
||||||
|
reply->option, opt);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If reply represents success, return 1 without further action.
|
||||||
|
* If reply represents an error, consume the optional payload of
|
||||||
|
* the packet on ioc. Then return 0 for unsupported (so the client
|
||||||
|
* can fall back to other approaches), or -1 with errp set for other
|
||||||
|
* errors.
|
||||||
|
*/
|
||||||
|
static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
uint32_t len;
|
|
||||||
char *msg = NULL;
|
char *msg = NULL;
|
||||||
int result = -1;
|
int result = -1;
|
||||||
|
|
||||||
if (!(type & (1 << 31))) {
|
if (!(reply->type & (1 << 31))) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
|
if (reply->length) {
|
||||||
error_setg(errp, "failed to read option length");
|
if (reply->length > NBD_MAX_BUFFER_SIZE) {
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = be32_to_cpu(len);
|
|
||||||
if (len) {
|
|
||||||
if (len > NBD_MAX_BUFFER_SIZE) {
|
|
||||||
error_setg(errp, "server's error message is too long");
|
error_setg(errp, "server's error message is too long");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
msg = g_malloc(len + 1);
|
msg = g_malloc(reply->length + 1);
|
||||||
if (read_sync(ioc, msg, len) != len) {
|
if (read_sync(ioc, msg, reply->length) != reply->length) {
|
||||||
error_setg(errp, "failed to read option error message");
|
error_setg(errp, "failed to read option error message");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
msg[len] = '\0';
|
msg[reply->length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (reply->type) {
|
||||||
case NBD_REP_ERR_UNSUP:
|
case NBD_REP_ERR_UNSUP:
|
||||||
TRACE("server doesn't understand request %" PRIx32
|
TRACE("server doesn't understand request %" PRIx32
|
||||||
", attempting fallback", opt);
|
", attempting fallback", reply->option);
|
||||||
result = 0;
|
result = 0;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
case NBD_REP_ERR_POLICY:
|
case NBD_REP_ERR_POLICY:
|
||||||
error_setg(errp, "Denied by server for option %" PRIx32, opt);
|
error_setg(errp, "Denied by server for option %" PRIx32,
|
||||||
|
reply->option);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NBD_REP_ERR_INVALID:
|
case NBD_REP_ERR_INVALID:
|
||||||
error_setg(errp, "Invalid data length for option %" PRIx32, opt);
|
error_setg(errp, "Invalid data length for option %" PRIx32,
|
||||||
|
reply->option);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NBD_REP_ERR_TLS_REQD:
|
case NBD_REP_ERR_TLS_REQD:
|
||||||
error_setg(errp, "TLS negotiation required before option %" PRIx32,
|
error_setg(errp, "TLS negotiation required before option %" PRIx32,
|
||||||
opt);
|
reply->option);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error_setg(errp, "Unknown error code when asking for option %" PRIx32,
|
error_setg(errp, "Unknown error code when asking for option %" PRIx32,
|
||||||
opt);
|
reply->option);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,58 +211,29 @@ static int nbd_handle_reply_err(QIOChannel *ioc, uint32_t opt, uint32_t type,
|
|||||||
|
|
||||||
static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
||||||
{
|
{
|
||||||
uint64_t magic;
|
nbd_opt_reply reply;
|
||||||
uint32_t opt;
|
|
||||||
uint32_t type;
|
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
uint32_t namelen;
|
uint32_t namelen;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
*name = NULL;
|
*name = NULL;
|
||||||
if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) {
|
||||||
error_setg(errp, "failed to read list option magic");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
magic = be64_to_cpu(magic);
|
error = nbd_handle_reply_err(ioc, &reply, errp);
|
||||||
if (magic != NBD_REP_MAGIC) {
|
|
||||||
error_setg(errp, "Unexpected option list magic");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
|
|
||||||
error_setg(errp, "failed to read list option");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
opt = be32_to_cpu(opt);
|
|
||||||
if (opt != NBD_OPT_LIST) {
|
|
||||||
error_setg(errp, "Unexpected option type %" PRIx32 " expected %x",
|
|
||||||
opt, NBD_OPT_LIST);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
|
|
||||||
error_setg(errp, "failed to read list option type");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
type = be32_to_cpu(type);
|
|
||||||
error = nbd_handle_reply_err(ioc, opt, type, errp);
|
|
||||||
if (error <= 0) {
|
if (error <= 0) {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
len = reply.length;
|
||||||
|
|
||||||
if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) {
|
if (reply.type == NBD_REP_ACK) {
|
||||||
error_setg(errp, "failed to read option length");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
len = be32_to_cpu(len);
|
|
||||||
|
|
||||||
if (type == NBD_REP_ACK) {
|
|
||||||
if (len != 0) {
|
if (len != 0) {
|
||||||
error_setg(errp, "length too long for option end");
|
error_setg(errp, "length too long for option end");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else if (type == NBD_REP_SERVER) {
|
} else if (reply.type == NBD_REP_SERVER) {
|
||||||
if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
|
if (len < sizeof(namelen) || len > NBD_MAX_BUFFER_SIZE) {
|
||||||
error_setg(errp, "incorrect option length");
|
error_setg(errp, "incorrect option length %" PRIu32, len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
|
if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) {
|
||||||
@ -240,7 +275,7 @@ static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
|
error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x",
|
||||||
type, NBD_REP_SERVER);
|
reply.type, NBD_REP_SERVER);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
@ -251,24 +286,10 @@ static int nbd_receive_query_exports(QIOChannel *ioc,
|
|||||||
const char *wantname,
|
const char *wantname,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
|
|
||||||
uint32_t opt = cpu_to_be32(NBD_OPT_LIST);
|
|
||||||
uint32_t length = 0;
|
|
||||||
bool foundExport = false;
|
bool foundExport = false;
|
||||||
|
|
||||||
TRACE("Querying export list");
|
TRACE("Querying export list");
|
||||||
if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) {
|
||||||
error_setg(errp, "Failed to send list option magic");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
|
|
||||||
error_setg(errp, "Failed to send list option number");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
|
|
||||||
error_setg(errp, "Failed to send list option length");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,72 +335,29 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
|
|||||||
QCryptoTLSCreds *tlscreds,
|
QCryptoTLSCreds *tlscreds,
|
||||||
const char *hostname, Error **errp)
|
const char *hostname, Error **errp)
|
||||||
{
|
{
|
||||||
uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC);
|
nbd_opt_reply reply;
|
||||||
uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS);
|
|
||||||
uint32_t length = 0;
|
|
||||||
uint32_t type;
|
|
||||||
QIOChannelTLS *tioc;
|
QIOChannelTLS *tioc;
|
||||||
struct NBDTLSHandshakeData data = { 0 };
|
struct NBDTLSHandshakeData data = { 0 };
|
||||||
|
|
||||||
TRACE("Requesting TLS from server");
|
TRACE("Requesting TLS from server");
|
||||||
if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
if (nbd_send_option_request(ioc, NBD_OPT_STARTTLS, 0, NULL, errp) < 0) {
|
||||||
error_setg(errp, "Failed to send option magic");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
|
|
||||||
error_setg(errp, "Failed to send option number");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
|
|
||||||
error_setg(errp, "Failed to send option length");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE("Getting TLS reply from server1");
|
|
||||||
if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
|
||||||
error_setg(errp, "failed to read option magic");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
magic = be64_to_cpu(magic);
|
|
||||||
if (magic != NBD_REP_MAGIC) {
|
|
||||||
error_setg(errp, "Unexpected option magic");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
TRACE("Getting TLS reply from server2");
|
|
||||||
if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
|
|
||||||
error_setg(errp, "failed to read option");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
opt = be32_to_cpu(opt);
|
|
||||||
if (opt != NBD_OPT_STARTTLS) {
|
|
||||||
error_setg(errp, "Unexpected option type %" PRIx32 " expected %x",
|
|
||||||
opt, NBD_OPT_STARTTLS);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Getting TLS reply from server");
|
TRACE("Getting TLS reply from server");
|
||||||
if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) {
|
if (nbd_receive_option_reply(ioc, NBD_OPT_STARTTLS, &reply, errp) < 0) {
|
||||||
error_setg(errp, "failed to read option type");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
type = be32_to_cpu(type);
|
|
||||||
if (type != NBD_REP_ACK) {
|
if (reply.type != NBD_REP_ACK) {
|
||||||
error_setg(errp, "Server rejected request to start TLS %" PRIx32,
|
error_setg(errp, "Server rejected request to start TLS %" PRIx32,
|
||||||
type);
|
reply.type);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TRACE("Getting TLS reply from server");
|
if (reply.length != 0) {
|
||||||
if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) {
|
|
||||||
error_setg(errp, "failed to read option length");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
length = be32_to_cpu(length);
|
|
||||||
if (length != 0) {
|
|
||||||
error_setg(errp, "Start TLS response was not zero %" PRIu32,
|
error_setg(errp, "Start TLS response was not zero %" PRIu32,
|
||||||
length);
|
reply.length);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,8 +445,6 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
|||||||
|
|
||||||
if (magic == NBD_OPTS_MAGIC) {
|
if (magic == NBD_OPTS_MAGIC) {
|
||||||
uint32_t clientflags = 0;
|
uint32_t clientflags = 0;
|
||||||
uint32_t opt;
|
|
||||||
uint32_t namesize;
|
|
||||||
uint16_t globalflags;
|
uint16_t globalflags;
|
||||||
bool fixedNewStyle = false;
|
bool fixedNewStyle = false;
|
||||||
|
|
||||||
@ -518,28 +494,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags,
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* write the export name */
|
/* write the export name request */
|
||||||
magic = cpu_to_be64(magic);
|
if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, name,
|
||||||
if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) {
|
errp) < 0) {
|
||||||
error_setg(errp, "Failed to send export name magic");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
opt = cpu_to_be32(NBD_OPT_EXPORT_NAME);
|
|
||||||
if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) {
|
|
||||||
error_setg(errp, "Failed to send export name option number");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
namesize = cpu_to_be32(strlen(name));
|
|
||||||
if (write_sync(ioc, &namesize, sizeof(namesize)) !=
|
|
||||||
sizeof(namesize)) {
|
|
||||||
error_setg(errp, "Failed to send export name length");
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (write_sync(ioc, (char *)name, strlen(name)) != strlen(name)) {
|
|
||||||
error_setg(errp, "Failed to send export name");
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read the response */
|
||||||
if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
|
if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) {
|
||||||
error_setg(errp, "Failed to read export length");
|
error_setg(errp, "Failed to read export length");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
#define NBD_REPLY_MAGIC 0x67446698
|
#define NBD_REPLY_MAGIC 0x67446698
|
||||||
#define NBD_OPTS_MAGIC 0x49484156454F5054LL
|
#define NBD_OPTS_MAGIC 0x49484156454F5054LL
|
||||||
#define NBD_CLIENT_MAGIC 0x0000420281861253LL
|
#define NBD_CLIENT_MAGIC 0x0000420281861253LL
|
||||||
#define NBD_REP_MAGIC 0x3e889045565a9LL
|
#define NBD_REP_MAGIC 0x0003e889045565a9LL
|
||||||
|
|
||||||
#define NBD_SET_SOCK _IO(0xab, 0)
|
#define NBD_SET_SOCK _IO(0xab, 0)
|
||||||
#define NBD_SET_BLKSIZE _IO(0xab, 1)
|
#define NBD_SET_BLKSIZE _IO(0xab, 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user