From: Wouter Verhelst Date: Sat, 5 May 2012 09:04:39 +0000 (+0200) Subject: Implement "list exports" message X-Git-Tag: nbd-3.1~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=626c2a37b653a760a72d9ab9e011fd50d5838cf9;p=platform%2Fupstream%2Fnbd.git Implement "list exports" message Previously, there was no way for the client to get a list of all available exports from an nbd-server. This implements that. Additionally, it implements a way for the server to understand more options than just the one "NBD_OPT_EXPORT_NAME" we did before, and proper handling of "sorry, I don't know what you're talking about". --- diff --git a/cliserv.h b/cliserv.h index f2c1b37..729e139 100644 --- a/cliserv.h +++ b/cliserv.h @@ -56,6 +56,7 @@ typedef unsigned long long u64; u64 cliserv_magic = 0x00420281861253LL; u64 opts_magic = 0x49484156454F5054LL; +u64 rep_magic = 0x3e889045565a9LL; #define INIT_PASSWD "NBDMAGIC" #define INFO(a) do { } while(0) @@ -148,4 +149,20 @@ u64 ntohll(u64 a) { * served */ /* Options that the client can select to the server */ -#define NBD_OPT_EXPORT_NAME (1 << 0) /* Client wants to select a named export (is followed by length and name of export) */ +#define NBD_OPT_EXPORT_NAME (1) /** Client wants to select a named export (is followed by name of export) */ +#define NBD_OPT_ABORT (2) /** Client wishes to abort negotiation */ +#define NBD_OPT_LIST (3) /** Client request list of supported exports (not followed by data) */ + +/* Replies the server can send during negotiation */ +#define NBD_REP_ACK (1) /** ACK a request. Data: option number to be acked */ +#define NBD_REP_SERVER (2) /** Reply to NBD_OPT_LIST (one of these per server; must be followed by NBD_REP_ACK to signal the end of the list */ +#define NBD_REP_FLAG_ERROR (1 << 31) /** If the high bit is set, the reply is an error */ +#define NBD_REP_ERR_UNSUP (1 | NBD_REP_FLAG_ERROR) /** Client requested an option not understood by this version of the server */ +#define NBD_REP_ERR_POLICY (2 | NBD_REP_FLAG_ERROR) /** Client requested an option not allowed by server configuration. (e.g., the option was disabled) */ +#define NBD_REP_ERR_INVALID (3 | NBD_REP_FLAG_ERROR) /** Client issued an invalid request */ +#define NBD_REP_ERR_PLATFORM (4 | NBD_REP_FLAG_ERROR) /** Option not supported on this platform */ + +/* Global flags */ +#define NBD_FLAG_FIXED_NEWSTYLE (1 << 0) /* new-style export that actually supports extending */ +/* Flags from client to server. Only one such option currently. */ +#define NBD_FLAG_C_FIXED_NEWSTYLE NBD_FLAG_FIXED_NEWSTYLE diff --git a/doc/proto.txt b/doc/proto.txt index c115157..320111f 100644 --- a/doc/proto.txt +++ b/doc/proto.txt @@ -107,7 +107,8 @@ production purposes. S: "NBDMAGIC" (as in the old style handshake) S: 0x49484156454F5054 (note different magic number) -S: 16 bits of zero (reserved for future use) +S: 16 bits of zero (bits 1-15 reserved for future use; bit 0 in use to + signal fixed newstyle (see below)) C: 32 bits of zero (reserved for future use) This completes the initial phase of negotiation; the client and server @@ -123,11 +124,9 @@ The generic format of setting an option is as follows: C: 0x49484156454F5054 (note same new-style handshake's magic number) C: 32 bits denoting the chosen option (NBD_OPT_EXPORT_NAME is the only - possible value currently) + possible value in this version of the protocol) C: unsigned 32 bit length of option data C: (any data needed for the chosen option) -S: (any response as needed and defined by the chosen option; currently - this does not happen). The presence of the option length in every option allows the server to skip any options presented by the client that it does not @@ -156,25 +155,143 @@ signalling that an extra flag field will follow, to which the client will have to reply with a flag field of its own before the extra flags are sent. This is not yet implemented. -Flag bits ---------- - -bit 0 - NBD_FLAG_HAS_FLAGS -should always be 1 - -bit 1 - NBD_FLAG_READ_ONLY -should be set to 1 if the export is read-only - -bit 2 - NBD_FLAG_SEND_FLUSH -should be set to 1 if the server supports NBD_CMD_FLUSH commands - -bit 3 - NBD_FLAG_SEND_FUA -should be set to 1 if the server supports the NBD_CMD_FLAG_FUA flag - -bit 4 - NBD_FLAG_ROTATIONAL -should be set to 1 to let the client schedule I/O accesses as for a -rotational medium - -bit 5 - NBD_FLAG_SEND_TRIM -should be set to 1 if the server supports NBD_CMD_TRIM commands +Fixed 'new' style handshake +--------------------------- + +Unfortunately, due to a mistake on my end, the server would immediately +close the connection when it saw an option it did not understand, rather +than signalling this fact to the client, which would've allowed it to +retry; and replies from the server were not structured either, which +meant that if the server were to send something the client did not +understand, it would have to abort negotiation as well. + +To fix these two issues, the handshake has been extended once more: + +- The server will set bit 0 of its first set of reserved flags, to + signal that it supports this version of the protocol. +- The client should reply with bit 0 set in its reserved field too, + though its side of the protocol does not change incompatibly. +- The client may now send other options to the server as appropriate, in + the generic format for sending an option as described above. +- The server will reply to any option apart from NBD_OPT_EXPORT_NAME + with reply packets in the following format: + +S: 64 bits, 0x3e889045565a9 (magic number for replies) +S: 32 bits, the option as sent by the client to which this is a reply + packet. +S: 32 bits, denoting reply type (e.g., NBD_REP_ACK to denote successful + completion, or NBD_REP_ERR_UNSUP to denote use of an option not known + by this server +S: 32 bits, length of the reply. This may be zero for some replies, in + which case the next field is not sent +S: any data as required by the reply (e.g., an export name in the case + of NBD_REP_SERVER + +As there is no unique number for client requests, clients who want to +differentiate between answers to two instances of the same option during +any negotiation must make sure they've seen the answer to an outstanding +request before sending the next one of the same type. + +Values +------ + +This section describes the meaning of constants (other than magic +numbers) in the protocol handshake. +Flag bits +- - - - - + +* Per-export (16 bits, sent after option haggling, or immediately after + the global flag field in oldstyle negotiation): + + bit 0 - NBD_FLAG_HAS_FLAGS + should always be 1 + + bit 1 - NBD_FLAG_READ_ONLY + should be set to 1 if the export is read-only + + bit 2 - NBD_FLAG_SEND_FLUSH + should be set to 1 if the server supports NBD_CMD_FLUSH commands + + bit 3 - NBD_FLAG_SEND_FUA + should be set to 1 if the server supports the NBD_CMD_FLAG_FUA flag + + bit 4 - NBD_FLAG_ROTATIONAL + should be set to 1 to let the client schedule I/O accesses as for a + rotational medium + + bit 5 - NBD_FLAG_SEND_TRIM + should be set to 1 if the server supports NBD_CMD_TRIM commands + +* Global flag bits (16 bits, after initial connection): + + bit 0 - NBD_FLAG_FIXED_NEWSTYLE + should be set by servers that support the fixed newstyle protocol + +* Client (after initial connection and after receiving flags from + server): + + bit 0 - NBD_FLAG_C_FIXED_NEWSTYLE + Should be set by clients that support the fixed newstyle protocol. + Servers may choose to honour fixed newstyle from clients that didn't + set this bit, but relying on this isn't recommended. + +Option types +- - - - - - - + +* NBD_OPT_EXPORT_NAME (1) + Choose the export which the client would like to use, and end option + haggling. Data: name of the export, free-form UTF8 text (subject to + limitations by server implementation). If the chosen export does not + exist, the server closes the connection. + +* NBD_OPT_LIST (2) + Returns a number of NBD_REP_SERVER replies, one for each export, + followed by an NBD_REP_ACK. + +* NBD_OPT_ABORT (3) + Abort negotiation and close the connection. Optional. + +Reply types +- - - - - - + +* NBD_REP_ACK (1) + Will be sent by the server when it accepts the option, or when sending + data related to the option (in the case of NBD_OPT_LIST) has finished. + No data. + +* NBD_REP_SERVER (2) + A description of an export. Data: + - 32 bits, length of name + - Name of the export, as expected by NBD_OPT_EXPORT_NAME + - If length of name < (length of reply as sent in the reply packet + header - 4), then the rest of the data contains some undefined + implementation-specific details about the export. This is not + currently implemented, but future versions of nbd-server may send + along some details about the export. If the client did not + explicitly request otherwise, these details are defined to be UTF-8 + encoded data suitable for direct display to a human being. + +There are a number of error reply types, all of which are denoted by +having bit 31 set. All error replies may have some data set, in which +case that data is an error message suitable for display to the user. + +* NBD_REP_ERR_UNSUP (2^31 + 1) + The option sent by the client is unknown by this server + implementation (e.g., because the server is too old, or from another + source). + +* NBD_REP_ERR_POLICY (2^31 + 2) + The option sent by the client is known by this server and + syntactically valid, but server-side policy forbids the server to + allow the option (e.g., the client sent NBD_OPT_LIST but server + configuration has that disabled) + +* NBD_REP_ERR_INVALID (2^31 + 3) + The option sent by the client is know by this server, but was + determined by the server to be syntactically invalid. For instance, + the client sent an NBD_OPT_LIST with nonzero data length. + +* NBD_REP_ERR_PLATFORM (2^31 + 4) + The option sent by the client is not supported on the platform on + which the server is running. Not currently used. diff --git a/nbd-client.c b/nbd-client.c index c69ea2c..30dc3e4 100644 --- a/nbd-client.c +++ b/nbd-client.c @@ -46,6 +46,8 @@ #include #endif +#define NBDC_DO_LIST 1 + int check_conn(char* devname, int do_print) { char buf[256]; char* p; @@ -124,7 +126,94 @@ int opennet(char *name, char* portstr, int sdp) { return sock; } -void negotiate(int sock, u64 *rsize64, u32 *flags, char* name) { +void ask_list(int sock) { + uint32_t opt; + uint32_t opt_server; + uint32_t len; + uint32_t reptype; + uint64_t magic; + char buf[1024]; + + magic = ntohll(opts_magic); + if (write(sock, &magic, sizeof(magic)) < 0) + err("Failed/2.2: %m"); + + /* Ask for the list */ + opt = htonl(NBD_OPT_LIST); + if(write(sock, &opt, sizeof(opt)) < 0) { + err("writing list option failed: %m"); + } + /* Send the length (zero) */ + len = htonl(0); + if(write(sock, &len, sizeof(len)) < 0) { + err("writing length failed: %m"); + } + do { + memset(buf, 0, 1024); + if(read(sock, &magic, sizeof(magic)) < 0) { + err("Reading magic from server: %m"); + } + if(read(sock, &opt_server, sizeof(opt_server)) < 0) { + err("Reading option: %m"); + } + if(read(sock, &reptype, sizeof(reptype)) <0) { + err("Reading reply from server: %m"); + } + if(read(sock, &len, sizeof(len)) < 0) { + err("Reading length from server: %m"); + } + magic=ntohll(magic); + len=ntohl(len); + reptype=ntohl(reptype); + if(magic != rep_magic) { + err("Not enough magic from server"); + } + if(reptype & NBD_REP_FLAG_ERROR) { + switch(reptype) { + case NBD_REP_ERR_POLICY: + fprintf(stderr, "\nE: listing not allowed by server.\n"); + break; + default: + fprintf(stderr, "\nE: unexpected error from server.\n"); + break; + } + if(len) { + if(read(sock, buf, len) < 0) { + fprintf(stderr, "\nE: could not read error message from server\n"); + } + fprintf(stderr, "Server said: %s\n", buf); + } + exit(EXIT_FAILURE); + } else { + if(len) { + if(reptype != NBD_REP_SERVER) { + err("Server sent us a reply we don't understand!"); + } + if(read(sock, &len, sizeof(len)) < 0) { + fprintf(stderr, "\nE: could not read export name length from server\n"); + exit(EXIT_FAILURE); + } + len=ntohl(len); + if(read(sock, buf, len) < 0) { + fprintf(stderr, "\nE: could not read export name from server\n"); + exit(EXIT_FAILURE); + } + printf("%s\n", buf); + } + } + } while(reptype != NBD_REP_ACK); + opt=htonl(NBD_OPT_ABORT); + len=htonl(0); + magic=htonll(opts_magic); + if (write(sock, &magic, sizeof(magic)) < 0) + err("Failed/2.2: %m"); + if (write(sock, &opt, sizeof(opt)) < 0) + err("Failed writing abort"); + if (write(sock, &len, sizeof(len)) < 0) + err("Failed writing length"); +} + +void negotiate(int sock, u64 *rsize64, u32 *flags, char* name, uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts) { u64 magic, size64; uint16_t tmp; char buf[256] = "\0\0\0\0\0\0\0\0\0"; @@ -143,7 +232,6 @@ void negotiate(int sock, u64 *rsize64, u32 *flags, char* name) { if(name) { uint32_t opt; uint32_t namesize; - uint32_t reserved = 0; if (magic != opts_magic) err("Not enough opts_magic"); @@ -152,15 +240,28 @@ void negotiate(int sock, u64 *rsize64, u32 *flags, char* name) { err("Failed reading flags: %m"); } *flags = ((u32)ntohs(tmp)); + if((needed_flags & *flags) != needed_flags) { + /* There's currently really only one reason why this + * check could possibly fail, but we may need to change + * this error message in the future... */ + fprintf(stderr, "\nE: Server does not support listing exports\n"); + exit(EXIT_FAILURE); + } - /* reserved for future use*/ - if (write(sock, &reserved, sizeof(reserved)) < 0) + client_flags = htonl(client_flags); + if (write(sock, &client_flags, sizeof(client_flags)) < 0) err("Failed/2.1: %m"); + if(do_opts & NBDC_DO_LIST) { + ask_list(sock); + exit(EXIT_SUCCESS); + } + /* Write the export name that we're after */ - magic = ntohll(opts_magic); + magic = htonll(opts_magic); if (write(sock, &magic, sizeof(magic)) < 0) err("Failed/2.2: %m"); + opt = ntohl(NBD_OPT_EXPORT_NAME); if (write(sock, &opt, sizeof(opt)) < 0) err("Failed/2.3: %m"); @@ -283,6 +384,7 @@ void usage(char* errmsg, ...) { fprintf(stderr, "Or : nbd-client -d nbd_device\n"); fprintf(stderr, "Or : nbd-client -c nbd_device\n"); fprintf(stderr, "Or : nbd-client -h|--help\n"); + fprintf(stderr, "Or : nbd-client -l|--list host\n"); fprintf(stderr, "Default value for blocksize is 1024 (recommended for ethernet)\n"); fprintf(stderr, "Allowed values for blocksize are 512,1024,2048,4096\n"); /* will be checked in kernel :) */ fprintf(stderr, "Note, that kernel 2.4.2 and older ones do not work correctly with\n"); @@ -328,11 +430,15 @@ int main(int argc, char *argv[]) { int c; int nonspecial=0; char* name=NULL; + uint32_t needed_flags; + uint32_t cflags; + uint32_t opts; struct option long_options[] = { { "block-size", required_argument, NULL, 'b' }, { "check", required_argument, NULL, 'c' }, { "disconnect", required_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, + { "list", no_argument, NULL, 'l' }, { "name", required_argument, NULL, 'N' }, { "nofork", no_argument, NULL, 'n' }, { "persist", no_argument, NULL, 'p' }, @@ -344,7 +450,7 @@ int main(int argc, char *argv[]) { logging(); - while((c=getopt_long_only(argc, argv, "-b:c:d:hnN:pSst:", long_options, NULL))>=0) { + while((c=getopt_long_only(argc, argv, "-b:c:d:hlnN:pSst:", long_options, NULL))>=0) { switch(c) { case 1: // non-option argument @@ -399,6 +505,14 @@ int main(int argc, char *argv[]) { case 'h': usage(NULL); exit(EXIT_SUCCESS); + case 'l': + needed_flags |= NBD_FLAG_FIXED_NEWSTYLE; + cflags |= NBD_FLAG_C_FIXED_NEWSTYLE; + opts |= NBDC_DO_LIST; + name=""; + nbddev=""; + port = NBD_DEFAULT_PORT; + break; case 'n': nofork=1; break; @@ -432,13 +546,14 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + sock = opennet(hostname, port, sdp); + + negotiate(sock, &size64, &flags, name, needed_flags, cflags, opts); + nbd = open(nbddev, O_RDWR); if (nbd < 0) err("Cannot open NBD: %m\nPlease ensure the 'nbd' module is loaded."); - sock = opennet(hostname, port, sdp); - - negotiate(sock, &size64, &flags, name); setsizes(nbd, size64, blocksize, flags); set_timeout(nbd, timeout); finish_sock(sock, nbd, swap); @@ -489,7 +604,7 @@ int main(int argc, char *argv[]) { close(sock); close(nbd); sock = opennet(hostname, port, sdp); nbd = open(nbddev, O_RDWR); - negotiate(sock, &new_size, &new_flags, name); + negotiate(sock, &new_size, &new_flags, name, needed_flags, cflags, opts); if (size64 != new_size) { err("Size of the device changed. Bye"); } diff --git a/nbd-server.c b/nbd-server.c index fd44659..bd8250a 100644 --- a/nbd-server.c +++ b/nbd-server.c @@ -170,9 +170,11 @@ int dontfork = 0; #define F_ROTATIONAL 512 /**< Whether server wants the client to implement the elevator algorithm */ #define F_TEMPORARY 1024 /**< Whether the backing file is temporary and should be created then unlinked */ #define F_TRIM 2048 /**< Whether server wants TRIM (discard) to be sent by the client */ +#define F_FIXED 4096 /**< Client supports fixed new-style protocol (and can thus send us extra options */ /** Global flags: */ #define F_OLDSTYLE 1 /**< Allow oldstyle (port-based) exports */ +#define F_LIST 2 /**< Allow clients to list the exports on a server */ GHashTable *children; char pidfname[256]; /**< name of our PID file */ char pidftemplate[256]; /**< template to be used for the filename of the PID file */ @@ -254,6 +256,7 @@ typedef struct { u32 *difmap; /**< see comment on the global difmap for this one */ gboolean modern; /**< client was negotiated using modern negotiation protocol */ int transactionlogfd;/**< fd for transaction log */ + int clientfeats; /**< Features supported by this client */ } CLIENT; /** @@ -873,6 +876,7 @@ GArray* parse_cfile(gchar* f, bool have_global, GError** e) { { "listenaddr", FALSE, PARAM_STRING, &modern_listen, 0 }, { "port", FALSE, PARAM_STRING, &modernport, 0 }, { "includedir", FALSE, PARAM_STRING, &cfdir, 0 }, + { "allowlist", FALSE, PARAM_BOOL, &glob_flags, F_LIST }, }; PARAM* p=gp; int p_size=sizeof(gp)/sizeof(PARAM); @@ -1504,6 +1508,77 @@ int exptrim(struct nbd_request* req, CLIENT* client) { return 0; } +static void send_reply(uint32_t opt, int net, uint32_t reply_type, size_t datasize, void* data) { + uint64_t magic = htonll(0x3e889045565a9LL); + reply_type = htonl(reply_type); + uint32_t datsize = htonl(datasize); + struct iovec v_data[] = { + { &magic, sizeof(magic) }, + { &opt, sizeof(opt) }, + { &reply_type, sizeof(reply_type) }, + { &datsize, sizeof(datsize) }, + { data, datasize }, + }; + writev(net, v_data, 5); +} + +static CLIENT* handle_export_name(uint32_t opt, int net, GArray* servers, uint32_t cflags) { + uint32_t namelen; + char* name; + int i; + + if (read(net, &namelen, sizeof(namelen)) < 0) + err("Negotiation failed/7: %m"); + namelen = ntohl(namelen); + name = malloc(namelen+1); + name[namelen]=0; + if (read(net, name, namelen) < 0) + err("Negotiation failed/8: %m"); + for(i=0; ilen; i++) { + SERVER* serve = &(g_array_index(servers, SERVER, i)); + if(!strcmp(serve->servename, name)) { + CLIENT* client = g_new0(CLIENT, 1); + client->server = serve; + client->exportsize = OFFT_MAX; + client->net = net; + client->modern = TRUE; + client->transactionlogfd = -1; + client->clientfeats = cflags; + free(name); + return client; + } + } + free(name); + return NULL; +} + +static void handle_list(uint32_t opt, int net, GArray* servers, uint32_t cflags) { + uint32_t len; + int i; + char buf[1024]; + char *ptr = buf + sizeof(len); + + if (read(net, &len, sizeof(len)) < 0) + err("Negotiation failed/8: %m"); + len = ntohl(len); + if(len) { + send_reply(opt, net, NBD_REP_ERR_INVALID, 0, NULL); + } + if(!(glob_flags & F_LIST)) { + send_reply(opt, net, NBD_REP_ERR_POLICY, 0, NULL); + err_nonfatal("Client tried disallowed list option"); + return; + } + for(i=0; ilen; i++) { + SERVER* serve = &(g_array_index(servers, SERVER, i)); + len = strlen(serve->servename); + memcpy(buf, &len, sizeof(len)); + strcpy(ptr, serve->servename); + send_reply(opt, net, NBD_REP_SERVER, len+sizeof(len), buf); + } + send_reply(opt, net, NBD_REP_ACK, 0, NULL); +} + /** * Do the initial negotiation. * @@ -1518,6 +1593,9 @@ CLIENT* negotiate(int net, CLIENT *client, GArray* servers, int phase) { memset(zeros, '\0', sizeof(zeros)); assert(((phase & NEG_INIT) && (phase & NEG_MODERN)) || client); + if(phase & NEG_MODERN) { + smallflags |= NBD_FLAG_FIXED_NEWSTYLE; + } if(phase & NEG_INIT) { /* common */ if (write(net, INIT_PASSWD, 8) < 0) { @@ -1540,54 +1618,50 @@ CLIENT* negotiate(int net, CLIENT *client, GArray* servers, int phase) { } if ((phase & NEG_MODERN) && (phase & NEG_INIT)) { /* modern */ - uint32_t reserved; + uint32_t cflags; uint32_t opt; - uint32_t namelen; - char* name; - int i; if(!servers) err("programmer error"); + smallflags = htons(smallflags); if (write(net, &smallflags, sizeof(uint16_t)) < 0) - err("Negotiation failed/3: %m"); - if (read(net, &reserved, sizeof(reserved)) < 0) - err("Negotiation failed/4: %m"); - if (read(net, &magic, sizeof(magic)) < 0) - err("Negotiation failed/5: %m"); - magic = ntohll(magic); - if(magic != opts_magic) { - close(net); - return NULL; - } - if (read(net, &opt, sizeof(opt)) < 0) - err("Negotiation failed/6: %m"); - opt = ntohl(opt); - if(opt != NBD_OPT_EXPORT_NAME) { + err_nonfatal("Negotiation failed/3: %m"); + if (read(net, &cflags, sizeof(cflags)) < 0) + err_nonfatal("Negotiation failed/4: %m"); + cflags = htonl(cflags); + do { + if (read(net, &magic, sizeof(magic)) < 0) + err_nonfatal("Negotiation failed/5: %m"); + magic = ntohll(magic); + if(magic != opts_magic) { + close(net); + return NULL; + } + if (read(net, &opt, sizeof(opt)) < 0) + err_nonfatal("Negotiation failed/6: %m"); + opt = ntohl(opt); + switch(opt) { + case NBD_OPT_EXPORT_NAME: + // NBD_OPT_EXPORT_NAME must be the last + // selected option, so return from here + // if that is chosen. + return handle_export_name(opt, net, servers, cflags); + break; + case NBD_OPT_LIST: + handle_list(opt, net, servers, cflags); + break; + case NBD_OPT_ABORT: + // handled below + break; + default: + send_reply(opt, net, NBD_REP_ERR_UNSUP, 0, NULL); + break; + } + } while((opt != NBD_OPT_EXPORT_NAME) && (opt != NBD_OPT_ABORT)); + if(opt == NBD_OPT_ABORT) { close(net); return NULL; } - if (read(net, &namelen, sizeof(namelen)) < 0) - err("Negotiation failed/7: %m"); - namelen = ntohl(namelen); - name = malloc(namelen+1); - name[namelen]=0; - if (read(net, name, namelen) < 0) - err("Negotiation failed/8: %m"); - for(i=0; ilen; i++) { - SERVER* serve = &(g_array_index(servers, SERVER, i)); - if(!strcmp(serve->servename, name)) { - CLIENT* client = g_new0(CLIENT, 1); - client->server = serve; - client->exportsize = OFFT_MAX; - client->net = net; - client->modern = TRUE; - client->transactionlogfd = -1; - free(name); - return client; - } - } - free(name); - return NULL; } /* common */ size_host = htonll((u64)(client->exportsize));