Factor out the bulk of client_serve_request, and add convenience converters in src/nbdtypes.c

This commit is contained in:
Alex Young
2012-05-31 17:44:11 +01:00
parent 949d7d6a72
commit 185a840e03
6 changed files with 475 additions and 86 deletions

57
src/nbdtypes.c Normal file
View File

@@ -0,0 +1,57 @@
#include "nbdtypes.h"
#include <string.h>
#include <endian.h>
/**
* We intentionally ignore the reserved 128 bytes at the end of the
* request, since there's nothing we can do with them.
*/
void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to )
{
memcpy( to->passwd, from->passwd, 8 );
to->magic = be64toh( from->magic );
to->size = be64toh( from->size );
}
void nbd_h2r_init( struct nbd_init * from, struct nbd_init_raw * to)
{
memcpy( to->passwd, from->passwd, 8 );
to->magic = htobe64( from->magic );
to->size = htobe64( from->size );
}
void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to )
{
to->magic = htobe32( from->magic );
to->type = htobe32( from->type );
memcpy( to->handle, from->handle, 8 );
to->from = htobe64( from->from );
to->len = htobe32( from->len );
}
void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to )
{
to->magic = be32toh( from->magic );
to->type = be32toh( from->type );
memcpy( to->handle, from->handle, 8 );
to->from = be64toh( from->from );
to->len = be32toh( from->len );
}
void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to )
{
to->magic = htobe32( from->magic );
to->error = htobe32( from->error );
memcpy( to->handle, from->handle, 8 );
}
void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to )
{
to->magic = be32toh( from->magic );
to->error = be32toh( from->error );
memcpy( to->handle, from->handle, 8 );
}

View File

@@ -1,6 +1,7 @@
#ifndef __NBDTYPES_H
#define __NBDTYPES_H
/* http://linux.derkeiler.com/Mailing-Lists/Kernel/2003-09/2332.html */
#define INIT_PASSWD "NBDMAGIC"
#define INIT_MAGIC 0x0000420281861253
@@ -10,27 +11,70 @@
#define REQUEST_WRITE 1
#define REQUEST_DISCONNECT 2
#ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE
#endif
#include <linux/types.h>
struct nbd_init {
#include <inttypes.h>
/* The _raw types are the types as they appear on the wire. Non-_raw
* types are in host-format.
* Conversion functions are _r2h_ for converting raw to host, and _h2r_
* for converting host to raw.
*/
struct nbd_init_raw {
char passwd[8];
__be64 magic;
__be64 size;
char reserved[128];
};
struct nbd_request_raw {
__be32 magic;
__be32 type; /* == READ || == WRITE */
char handle[8];
__be64 from;
__be32 len;
} __attribute__((packed));
struct nbd_reply_raw {
__be32 magic;
__be32 error; /* 0 = ok, else error */
char handle[8]; /* handle you got from request */
};
struct nbd_init {
char passwd[8];
uint64_t magic;
uint64_t size;
char reserved[128];
};
struct nbd_request {
__be32 magic;
__be32 type; /* == READ || == WRITE */
char handle[8];
__be64 from;
__be32 len;
uint32_t magic;
uint32_t type; /* == READ || == WRITE */
char handle[8];
uint64_t from;
uint32_t len;
} __attribute__((packed));
struct nbd_reply {
__be32 magic;
__be32 error; /* 0 = ok, else error */
char handle[8]; /* handle you got from request */
uint32_t magic;
uint32_t error; /* 0 = ok, else error */
char handle[8]; /* handle you got from request */
};
void nbd_r2h_init( struct nbd_init_raw * from, struct nbd_init * to );
void nbd_r2h_request( struct nbd_request_raw *from, struct nbd_request * to );
void nbd_r2h_reply( struct nbd_reply_raw * from, struct nbd_reply * to );
void nbd_h2r_init( struct nbd_init * from, struct nbd_init_raw * to);
void nbd_h2r_request( struct nbd_request * from, struct nbd_request_raw * to );
void nbd_h2r_reply( struct nbd_reply * from, struct nbd_reply_raw * to );
#endif

View File

@@ -2,7 +2,10 @@
#define __PARAMS_H
#define _GNU_SOURCE
#define _LARGEFILE64_SOURCE
#ifndef _LARGEFILE64_SOURCE
# define _LARGEFILE64_SOURCE
#endif
#include "parse.h"

View File

@@ -1,7 +1,7 @@
#include "nbdtypes.h"
#include "ioutil.h"
#include "util.h"
#include "params.h"
#include "params.h"
#include <stdlib.h>
#include <string.h>

View File

@@ -132,12 +132,13 @@ void write_not_zeroes(struct client_params* client, off64_t from, int len)
}
}
int client_serve_request(struct client_params* client)
/* Returns 1 if *request was filled with a valid request which we should
* try to honour. 0 otherwise. */
int client_read_request( struct client_params * client , struct nbd_request *out_request )
{
off64_t offset;
struct nbd_request request;
struct nbd_reply reply;
fd_set fds;
struct nbd_request_raw request_raw;
fd_set fds;
FD_ZERO(&fds);
FD_SET(client->socket, &fds);
@@ -146,53 +147,135 @@ int client_serve_request(struct client_params* client)
"select() failed");
if (FD_ISSET(client->serve->close_signal[0], &fds))
return 1;
if (readloop(client->socket, &request, sizeof(request)) == -1) {
return 0;
if (readloop(client->socket, &request_raw, sizeof(request_raw)) == -1) {
if (errno == 0) {
debug("EOF reading request");
return 1; /* neat point to close the socket */
return 0; /* neat point to close the socket */
}
else {
CLIENT_ERROR_ON_FAILURE(-1, "Error reading request");
}
}
nbd_r2h_request( &request_raw, out_request );
return 1;
}
int client_write_reply( struct client_params * client, struct nbd_request *request, int error )
{
struct nbd_reply reply;
struct nbd_reply_raw reply_raw;
reply.magic = REPLY_MAGIC;
reply.error = error;
memcpy( reply.handle, &request->handle, 8 );
nbd_h2r_reply( &reply, &reply_raw );
write( client->socket, &reply_raw, sizeof( reply_raw ) );
return 1;
}
int client_request_needs_reply( struct client_params * client, struct nbd_request request, int *request_err )
{
debug("request type %d", request.type);
reply.magic = htobe32(REPLY_MAGIC);
reply.error = htobe32(0);
memcpy(reply.handle, request.handle, 8);
debug("request type %d", be32toh(request.type));
if (be32toh(request.magic) != REQUEST_MAGIC)
CLIENT_ERROR("Bad magic %08x", be32toh(request.magic));
if (request.magic != REQUEST_MAGIC)
CLIENT_ERROR("Bad magic %08x", request.magic);
switch (be32toh(request.type))
switch (request.type)
{
case REQUEST_READ:
break;
case REQUEST_WRITE:
/* check it's not out of range */
if (be64toh(request.from) < 0 ||
be64toh(request.from)+be32toh(request.len) > client->serve->size) {
if (request.from < 0 ||
request.from+request.len > client->serve->size) {
debug("request read %ld+%d out of range",
be64toh(request.from),
be32toh(request.len)
request.from,
request.len
);
reply.error = htobe32(1);
write(client->socket, &reply, sizeof(reply));
client_write_reply( client, &request, 1 );
*request_err = 0;
return 0;
}
break;
case REQUEST_DISCONNECT:
debug("request disconnect");
return 1;
*request_err = 1;
return 0;
default:
CLIENT_ERROR("Unknown request %08x", be32toh(request.type));
CLIENT_ERROR("Unknown request %08x", request.type);
}
return 1;
}
void client_reply_to_read( struct client_params* client, struct nbd_request request )
{
off64_t offset;
debug("request read %ld+%d", request.from, request.len);
client_write_reply( client, &request, 0);
offset = request.from;
CLIENT_ERROR_ON_FAILURE(
sendfileloop(
client->socket,
client->fileno,
&offset,
request.len),
"sendfile failed from=%ld, len=%d",
offset,
request.len);
}
void client_reply_to_write( struct client_params* client, struct nbd_request request )
{
debug("request write %ld+%d", request.from, request.len);
if (client->serve->block_allocation_map) {
write_not_zeroes( client, request.from, request.len );
}
else {
CLIENT_ERROR_ON_FAILURE(
readloop(
client->socket,
client->mapped + request.from,
request.len),
"read failed from=%ld, len=%d",
request.from,
request.len );
dirty(client->serve, request.from, request.len);
}
client_write_reply( client, &request, 0);
}
void client_reply( struct client_params* client, struct nbd_request request )
{
switch (request.type) {
case REQUEST_READ:
client_reply_to_read( client, request );
break;
case REQUEST_WRITE:
client_reply_to_write( client, request );
break;
}
}
int client_lock_io( struct client_params * client )
{
CLIENT_ERROR_ON_FAILURE(
pthread_mutex_lock(&client->serve->l_io),
"Problem with I/O lock"
@@ -203,64 +286,43 @@ int client_serve_request(struct client_params* client)
pthread_mutex_unlock(&client->serve->l_io),
"Problem with I/O unlock"
);
return 1;
}
switch (be32toh(request.type))
{
case REQUEST_READ:
debug("request read %ld+%d", be64toh(request.from), be32toh(request.len));
write(client->socket, &reply, sizeof(reply));
offset = be64toh(request.from);
CLIENT_ERROR_ON_FAILURE(
sendfileloop(
client->socket,
client->fileno,
&offset,
be32toh(request.len)
),
"sendfile failed from=%ld, len=%d",
offset,
be32toh(request.len)
);
break;
case REQUEST_WRITE:
debug("request write %ld+%d", be64toh(request.from), be32toh(request.len));
if (client->serve->block_allocation_map) {
write_not_zeroes(
client,
be64toh(request.from),
be32toh(request.len)
);
}
else {
CLIENT_ERROR_ON_FAILURE(
readloop(
client->socket,
client->mapped + be64toh(request.from),
be32toh(request.len)
),
"read failed from=%ld, len=%d",
be64toh(request.from),
be32toh(request.len)
);
dirty(client->serve, be64toh(request.from), be32toh(request.len));
}
write(client->socket, &reply, sizeof(reply));
break;
return 0;
}
return 1;
}
void client_unlock_io( struct client_params * client )
{
CLIENT_ERROR_ON_FAILURE(
pthread_mutex_unlock(&client->serve->l_io),
"Problem with I/O unlock"
);
}
int client_serve_request(struct client_params* client)
{
struct nbd_request request;
int request_err;
if ( !client_read_request( client, &request ) ) { return 1; }
if ( !client_request_needs_reply( client, request, &request_err ) ) {
return request_err;
}
if ( client_lock_io( client ) ){
client_reply( client, request );
client_unlock_io( client );
} else {
return 1;
}
return 0;
}
void client_send_hello(struct client_params* client)
{
struct nbd_init init;

223
tests/check_nbdtypes.c Normal file
View File

@@ -0,0 +1,223 @@
#include <check.h>
#include "nbdtypes.h"
START_TEST(test_init_passwd)
{
struct nbd_init_raw init_raw;
struct nbd_init init;
memcpy( init_raw.passwd, INIT_PASSWD, 8 );
nbd_r2h_init( &init_raw, &init );
memset( init_raw.passwd, 0, 8 );
nbd_h2r_init( &init, &init_raw );
fail_unless( memcmp( init.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied." );
fail_unless( memcmp( init_raw.passwd, INIT_PASSWD, 8 ) == 0, "The password was not copied back." );
}
END_TEST
START_TEST(test_init_magic)
{
struct nbd_init_raw init_raw;
struct nbd_init init;
init_raw.magic = 12345;
nbd_r2h_init( &init_raw, &init );
fail_unless( be64toh( 12345 ) == init.magic, "Magic was not converted." );
init.magic = 67890;
nbd_h2r_init( &init, &init_raw );
fail_unless( htobe64( 67890 ) == init_raw.magic, "Magic was not converted back." );
}
END_TEST
START_TEST(test_init_size)
{
struct nbd_init_raw init_raw;
struct nbd_init init;
init_raw.size = 12345;
nbd_r2h_init( &init_raw, &init );
fail_unless( be64toh( 12345 ) == init.size, "Size was not converted." );
init.size = 67890;
nbd_h2r_init( &init, &init_raw );
fail_unless( htobe64( 67890 ) == init_raw.size, "Size was not converted back." );
}
END_TEST
START_TEST(test_request_magic )
{
struct nbd_request_raw request_raw;
struct nbd_request request;
request_raw.magic = 12345;
nbd_r2h_request( &request_raw, &request );
fail_unless( be32toh( 12345 ) == request.magic, "Magic was not converted." );
request.magic = 67890;
nbd_h2r_request( &request, &request_raw );
fail_unless( htobe32( 67890 ) == request_raw.magic, "Magic was not converted back." );
}
END_TEST
START_TEST(test_request_type )
{
struct nbd_request_raw request_raw;
struct nbd_request request;
request_raw.type = 12345;
nbd_r2h_request( &request_raw, &request );
fail_unless( be32toh( 12345 ) == request.type, "Type was not converted." );
request.type = 67890;
nbd_h2r_request( &request, &request_raw );
fail_unless( htobe32( 67890 ) == request_raw.type, "Type was not converted back." );
}
END_TEST
START_TEST(test_request_handle)
{
struct nbd_request_raw request_raw;
struct nbd_request request;
memcpy( request_raw.handle, "MYHANDLE", 8 );
nbd_r2h_request( &request_raw, &request );
memset( request_raw.handle, 0, 8 );
nbd_h2r_request( &request, &request_raw );
fail_unless( memcmp( request.handle, "MYHANDLE", 8 ) == 0, "The handle was not copied." );
fail_unless( memcmp( request_raw.handle, "MYHANDLE", 8 ) == 0, "The handle was not copied back." );
}
END_TEST
START_TEST(test_request_from )
{
struct nbd_request_raw request_raw;
struct nbd_request request;
request_raw.from = 12345;
nbd_r2h_request( &request_raw, &request );
fail_unless( be64toh( 12345 ) == request.from, "From was not converted." );
request.from = 67890;
nbd_h2r_request( &request, &request_raw );
fail_unless( htobe64( 67890 ) == request_raw.from, "From was not converted back." );
}
END_TEST
START_TEST(test_request_len )
{
struct nbd_request_raw request_raw;
struct nbd_request request;
request_raw.len = 12345;
nbd_r2h_request( &request_raw, &request );
fail_unless( be32toh( 12345 ) == request.len, "Type was not converted." );
request.len = 67890;
nbd_h2r_request( &request, &request_raw );
fail_unless( htobe32( 67890 ) == request_raw.len, "Type was not converted back." );
}
END_TEST
START_TEST(test_reply_magic )
{
struct nbd_reply_raw reply_raw;
struct nbd_reply reply;
reply_raw.magic = 12345;
nbd_r2h_reply( &reply_raw, &reply );
fail_unless( be32toh( 12345 ) == reply.magic, "Magic was not converted." );
reply.magic = 67890;
nbd_h2r_reply( &reply, &reply_raw );
fail_unless( htobe32( 67890 ) == reply_raw.magic, "Magic was not converted back." );
}
END_TEST
START_TEST(test_reply_error )
{
struct nbd_reply_raw reply_raw;
struct nbd_reply reply;
reply_raw.error = 12345;
nbd_r2h_reply( &reply_raw, &reply );
fail_unless( be32toh( 12345 ) == reply.error, "Error was not converted." );
reply.error = 67890;
nbd_h2r_reply( &reply, &reply_raw );
fail_unless( htobe32( 67890 ) == reply_raw.error, "Error was not converted back." );
}
END_TEST
START_TEST(test_reply_handle)
{
struct nbd_reply_raw reply_raw;
struct nbd_reply reply;
memcpy( reply_raw.handle, "MYHANDLE", 8 );
nbd_r2h_reply( &reply_raw, &reply );
memset( reply_raw.handle, 0, 8 );
nbd_h2r_reply( &reply, &reply_raw );
fail_unless( memcmp( reply.handle, "MYHANDLE", 8 ) == 0, "The handle was not copied." );
fail_unless( memcmp( reply_raw.handle, "MYHANDLE", 8 ) == 0, "The handle was not copied back." );
}
END_TEST
Suite *nbdtypes_suite()
{
Suite *s = suite_create( "nbdtypes" );
TCase *tc_init = tcase_create( "nbd_init" );
TCase *tc_request = tcase_create( "nbd_request" );
TCase *tc_reply = tcase_create( "nbd_reply" );
tcase_add_test( tc_init, test_init_passwd );
tcase_add_test( tc_init, test_init_magic );
tcase_add_test( tc_init, test_init_size );
tcase_add_test( tc_request, test_request_magic );
tcase_add_test( tc_request, test_request_type );
tcase_add_test( tc_request, test_request_handle );
tcase_add_test( tc_request, test_request_from );
tcase_add_test( tc_request, test_request_len );
tcase_add_test( tc_reply, test_reply_magic );
tcase_add_test( tc_reply, test_reply_error );
tcase_add_test( tc_reply, test_reply_handle );
suite_add_tcase( s, tc_init );
suite_add_tcase( s, tc_request );
suite_add_tcase( s, tc_reply );
return s;
}
int main(void)
{
int number_failed;
Suite *s = nbdtypes_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? 0 : 1;
}