diff --git a/src/nbdtypes.c b/src/nbdtypes.c new file mode 100644 index 0000000..51b57f9 --- /dev/null +++ b/src/nbdtypes.c @@ -0,0 +1,57 @@ +#include "nbdtypes.h" + +#include +#include + + +/** + * 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 ); +} diff --git a/src/nbdtypes.h b/src/nbdtypes.h index 499a4a1..f902aa1 100644 --- a/src/nbdtypes.h +++ b/src/nbdtypes.h @@ -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 -struct nbd_init { +#include + +/* 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 diff --git a/src/params.h b/src/params.h index 98d9325..b31aed1 100644 --- a/src/params.h +++ b/src/params.h @@ -2,7 +2,10 @@ #define __PARAMS_H #define _GNU_SOURCE -#define _LARGEFILE64_SOURCE + +#ifndef _LARGEFILE64_SOURCE +# define _LARGEFILE64_SOURCE +#endif #include "parse.h" diff --git a/src/readwrite.c b/src/readwrite.c index 9e006eb..36418f6 100644 --- a/src/readwrite.c +++ b/src/readwrite.c @@ -1,7 +1,7 @@ #include "nbdtypes.h" #include "ioutil.h" #include "util.h" -#include "params.h" +#include "params.h" #include #include diff --git a/src/serve.c b/src/serve.c index 71a1261..de3b096 100644 --- a/src/serve.c +++ b/src/serve.c @@ -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; diff --git a/tests/check_nbdtypes.c b/tests/check_nbdtypes.c new file mode 100644 index 0000000..d4f64bf --- /dev/null +++ b/tests/check_nbdtypes.c @@ -0,0 +1,223 @@ +#include + +#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; +} +