static unsigned char * handle_get_quad_freq (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length != 8) { fs_error(LOG_ERR, "get_quad_freq(%d) wrong length %u", segment, length); return fsp_error_new(segment, "wrong length"); } int index, count; memcpy(&index, content, sizeof(int)); memcpy(&count, content + sizeof(int), sizeof(int)); const char *filename; /* this branch has no frequency data */ filename = ""; int fd = open(filename, FS_O_NOATIME | O_RDONLY, 0); unsigned char *reply = message_new(FS_QUAD_FREQ, segment, count * sizeof(fs_quad_freq)); //ssize_t bytes = read(fd, reply + FS_HEADER, count * sizeof(fs_quad_freq)); ssize_t bytes = 0; unsigned int *l = (unsigned int *) (reply + 4); *l = bytes; close(fd); return reply; }
static unsigned char * handle_commit_quad (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length != sizeof(int)) { fs_error(LOG_ERR, "commit_quad(%d) missing flags", segment); return fsp_error_new(segment, "missing flags"); } int flags; memcpy(&flags, content, sizeof (flags)); int ret = fs_quad_import_commit(be, segment, flags, 1); if (ret) { fs_error(LOG_ERR, "commit_quad(%d) failed", segment); return fsp_error_new(segment, "quad commit failed"); } return message_new(FS_DONE_OK, segment, 0); }
static unsigned char * handle_new_models (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < sizeof(fs_rid)) { fs_error(LOG_ERR, "new_models(%d) missing model RIDs", segment); return fsp_error_new(segment, "missing model RIDs"); } fs_rid *models = (fs_rid *) content; int invalid_count = 0; for (int k= 0; k < (length / sizeof(fs_rid)); ++k) { if (FS_IS_URI(models[k])) { fs_backend_model_set_usage(be, segment, models[k], 0); } else { invalid_count++; } } fs_mhash_flush(be->models); if (invalid_count > 0) { return fsp_error_new(segment, "one or more model RIDs is not a URI"); } return message_new(FS_DONE_OK, segment, 0); }
static unsigned char * handle_insert_quad (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < 40) { fs_error(LOG_ERR, "insert_quad(%d) too short", segment); return fsp_error_new(segment, "too short"); } int flags; int count = (length - 8) / 32; fs_rid (*buffer)[4] = (fs_rid (*)[4]) (content + 8); memcpy(&flags, content, sizeof (flags)); int ret = fs_quad_import(be, segment, flags, count, buffer); if (ret) { fs_error(LOG_ERR, "insert_quad(%d) failed", segment); return fsp_error_new(segment, "quad insert failed"); } return NULL; /* no reply - semi-async */ }
static unsigned char * handle_get_data_size (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length > 0) { fs_error(LOG_ERR, "get_data_size(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } fs_data_size size = fs_get_data_size(be, segment); fs_old_data_size old_size; old_size.quads_s = size.quads_s; old_size.quads_o = size.quads_o; old_size.resources = size.resources; old_size.models_s = size.models_s; old_size.models_o = size.models_o; unsigned char *reply = message_new(FS_SIZE, segment, sizeof(old_size)); memcpy(reply + FS_HEADER, &old_size, sizeof(old_size)); return reply; }
static unsigned char * handle_resolve_attr (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { unsigned int count = length / sizeof(fs_rid); if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < sizeof(fs_rid)) { fs_error(LOG_ERR, "resolve_attr(%d) too short", segment); return fsp_error_new(segment, "too short"); } unsigned char *reply; fs_rid_vector v; fs_resource resources[count]; v.size = v.length = count; v.data = (fs_rid *) content; fs_resolve(be, segment, &v, resources); unsigned int k, serial_length = 0; for (k = 0; k < count; ++k) { if (resources[k].lex) { serial_length+= ((28 + strlen(resources[k].lex)) / 8); } else { serial_length+= 3; } } reply = message_new(FS_RESOURCE_ATTR_LIST, segment, 8 * serial_length); unsigned char *record = reply + FS_HEADER; for (k = 0; k < count; ++k) { unsigned int one_length; if (resources[k].lex) { one_length = ((28 + strlen(resources[k].lex)) / 8) * 8; } else { one_length = 24; } memcpy(record, &(resources[k].rid), sizeof (fs_rid)); memcpy(record + 8, &(resources[k].attr), sizeof (fs_rid)); memcpy(record + 16, &one_length, sizeof(one_length)); /* ASCII NUL is used to terminate strings on the wire */ if (resources[k].lex) { strcpy((char *) record + 20, resources[k].lex); } else { *(record + 20) = '\0'; } record += one_length; } return reply; }
static unsigned char * handle_bind_next (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { unsigned char *reply; if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length != 8) { fs_error(LOG_ERR, "bind_next(%d) wrong length %u", segment, length); return fsp_error_new(segment, "wrong length"); } unsigned int flags; int count; memcpy(&flags, content, sizeof (flags)); memcpy(&count, content + 4, sizeof (count)); fs_rid_vector **bindings; bindings = fs_bind_next(be, segment, flags, count); int k, cols = 0; for (k = 0; k < 4; ++k) { if (flags & 1 << k) cols++; } if (bindings == NULL) { /* NULL => no match */ reply = message_new(FS_NO_MATCH, segment, 0); cols = 0; } else if (cols == 0) { /* Zero columns => match with no binding */ reply = message_new(FS_BIND_LIST, segment, 0); } else { /* otherwise return bindings */ reply = message_new(FS_BIND_LIST, segment, bindings[0]->length * 8 * cols); unsigned char *data = reply + FS_HEADER; for (k= 0; k < cols; ++k) { memcpy(data, bindings[k]->data, bindings[k]->length * 8); data += bindings[k]->length * 8; } } for (k = 0; k < cols; ++k) { fs_rid_vector_free(bindings[k]); } free(bindings); return reply; }
static unsigned char * handle_price (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < 24) { fs_error(LOG_ERR, "price(%d) much too short", segment); return fsp_error_new(segment, "much too short"); } unsigned char *reply; unsigned long long int rows = 0; fs_rid_vector models, subjects, predicates, objects; unsigned int flags, value; memcpy(&flags, content, sizeof (flags)); memcpy(&value, content + 4, sizeof (models.length)); models.size = models.length = value / 8; memcpy(&value, content + 8, sizeof (subjects.length)); subjects.size = subjects.length = value / 8; memcpy(&value, content + 12, sizeof (predicates.length)); predicates.size = predicates.length = value / 8; memcpy(&value, content + 16, sizeof (objects.length)); objects.size = objects.length = value / 8; content += 24; if (length < (models.size + subjects.size + predicates.size + objects.size) * 8 + 24) { fs_error(LOG_ERR, "price(%d) too short", segment); return fsp_error_new(segment, "too short"); } models.data = (fs_rid *) content; content += models.length * 8; subjects.data = (fs_rid *) content; content += subjects.length * 8; predicates.data = (fs_rid *) content; content += predicates.length * 8; objects.data = (fs_rid *) content; rows = fs_bind_price(be, segment, flags, &models, &subjects, &predicates, &objects); reply = message_new(FS_ESTIMATED_ROWS, segment, 8); memcpy(reply + FS_HEADER, &rows, sizeof(rows)); return reply; }
static unsigned char * handle_lock (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { return fsp_error_new(segment, "radix backend does not support/require locking"); }
static unsigned char * handle_commit_resource (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length > 0) { fs_error(LOG_ERR, "commit_resource(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } fs_res_import_commit(be, segment, 1); return message_new(FS_DONE_OK, segment, 0); }
static unsigned char * handle_delete_quads (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < 32) { fs_error(LOG_ERR, "delete_quads(%d) much too short", segment); return fsp_error_new(segment, "much too short"); } fs_rid_vector models, subjects, predicates, objects; models.size = models.length = length / 32; subjects.size = subjects.length = length / 32; predicates.size = predicates.length = length / 32; objects.size = objects.length = length / 32; if (length < (models.size + subjects.size + predicates.size + objects.size) * 8) { fs_error(LOG_ERR, "delete_quads(%d) too short (%d < %d)", segment, length, (models.size + subjects.size + predicates.size + objects.size) * 8); return fsp_error_new(segment, "too short"); } models.data = (fs_rid *) content; content += models.length * 8; subjects.data = (fs_rid *) content; content += subjects.length * 8; predicates.data = (fs_rid *) content; content += predicates.length * 8; objects.data = (fs_rid *) content; fs_rid_vector *args[4] = { &models, &subjects, &predicates, &objects }; fs_delete_quads(be, args); /* FIXME, should check return value */ return message_new(FS_DONE_OK, 0, 0); }
static unsigned char * handle_get_size_reverse (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length > 0) { fs_error(LOG_ERR, "get_data_size(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } fs_data_size size = fs_get_data_size(be, segment); unsigned char *reply = message_new(FS_SIZE_REVERSE, segment, sizeof(size.quads_sr)); memcpy(reply + FS_HEADER, &size.quads_sr, sizeof(size.quads_sr)); return reply; }
static unsigned char * handle_auth (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { md5_state_t md5; if (length <= 16) { fs_error(LOG_ERR, "auth(%d) missing kbname", segment); return fsp_error_new(segment, "missing kbname"); } if (strncmp(be->db_name, (char *) content + 16, length - 16)) { fs_error(LOG_ERR, "auth(%d) wrong kbname", segment); return fsp_error_new(segment, "wrong kbname"); } if (be->salt) { unsigned char data[20], hash[16]; char string[33]; memcpy(data, (&be->salt), sizeof(be->salt)); memcpy(data + 4, content, 16); md5_init(&md5); md5_append(&md5, data, sizeof(data)); md5_finish(&md5, hash); sprintf(string, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]); if (strcmp(string, be->hash)) { fs_error(LOG_ERR, "auth(%d) invalid password", segment); return fsp_error_new(segment, "invalid password"); } } /* in-band signal, string of features */ unsigned char *reply = message_new(FS_DONE_OK, 0, sizeof(feature_string)); memcpy(reply + FS_HEADER, feature_string, sizeof(feature_string)); return reply; }
static unsigned char * handle_choose_segment (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (fs_backend_open_files(be, segment, O_RDWR | O_CREAT, FS_OPEN_ALL)) { fs_error(LOG_ERR, "failed to open files for segment %d", segment); return fsp_error_new(segment, "cannot open indexes"); } return message_new(FS_DONE_OK, 0, 0); }
static unsigned char * handle_get_query_times (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length > 0) { fs_error(LOG_ERR, "get_query_times(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } fs_query_timing timing = fs_get_query_times(be, segment); unsigned char *reply = message_new(FS_QUERY_TIMES, segment, sizeof(timing)); memcpy(reply + FS_HEADER, &timing, sizeof(timing)); return reply; }
static unsigned char * handle_delete_models (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < sizeof(fs_rid)) { fs_error(LOG_ERR, "delete_models(%d) missing model RIDs", segment); return fsp_error_new(segment, "missing model RIDs"); } fs_rid_vector models; models.size = models.length = length / sizeof(fs_rid); models.data = (fs_rid *) content; fs_delete_models(be, segment, &models); return message_new(FS_DONE_OK, segment, 0); }
static unsigned char * handle_stop_import (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length > 0) { fs_error(LOG_ERR, "stop_import(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } int ret = fs_stop_import(be, segment); if (ret) { return fsp_error_new(segment, "insert failed"); } return message_new(FS_DONE_OK, segment, 0); }
static unsigned char * handle_node_segments (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment != 0) { fs_error(LOG_ERR, "segments(%d) should only be sent with segment = 0", segment); return fsp_error_new(segment, "only to be sent to segment = 0"); } if (length > 0) { fs_error(LOG_ERR, "segments(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } char segments[be->segments]; fs_node_segments(be, segments); unsigned char *reply = message_new(FS_NODE_SEGMENT_LIST, be->segments, be->segments); memcpy(reply + FS_HEADER, segments, be->segments); return reply; }
static unsigned char * handle_or_fail(const char *name, fsp_backend_fn handle_fn, fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (handle_fn) { return handle_fn(be, segment, length, content); } else { kb_error(LOG_ERR, "no implementation for %s", name); return fsp_error_new(segment, "not implemented"); } }
static unsigned char * handle_bnode_alloc (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment != 0) { fs_error(LOG_ERR, "bnode_alloc(%d) should only be sent to segment zero", segment); return fsp_error_new(segment, "only to be sent to segment zero"); } if (length != sizeof(int)) { fs_error(LOG_ERR, "bnode_alloc(%d) missing count", segment); return fsp_error_new(segment, "missing count"); } int count; memcpy(&count, content, sizeof(count)); unsigned char *reply = message_new(FS_BNODE_RANGE, 0, 16); unsigned char *range = reply + FS_HEADER; fs_bnode_alloc(be, count, (fs_rid *) range, (fs_rid *) (range + sizeof(fs_rid))); return reply; }
static unsigned char * handle_start_import (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length > 0) { fs_error(LOG_ERR, "start_import(%d) extraneous content", segment); return fsp_error_new(segment, "extraneous content"); } float free_space = fs_free_disk(fs_backend_get_kb(be)); if (free_space < be->min_free) { fs_error(LOG_ERR, "segment %d only has %.1f%% free space", segment, free_space); return fsp_error_new(segment, "low disk space"); } fs_start_import(be, segment); return message_new(FS_DONE_OK, segment, 0); }
static unsigned char * handle_transaction (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { if (length != 1) { fs_error(LOG_ERR, "transaction(%d), missing flag", segment); return fsp_error_new(segment, "missing flag"); } int ret = fs_backend_transaction(be, segment, *content); if (ret == 2) { if (*content == FS_TRANS_BEGIN) { return fsp_error_new(segment, "transaction already begun"); } else { return fsp_error_new(segment, "transaction not begun"); } } else if (ret) { return fsp_error_new(segment, "transaction error"); } return message_new(FS_DONE_OK, 0, 0); }
static void child (int conn, fsp_backend *backend, fs_backend *be) { int auth = 0; while (1) { fs_segment segment; unsigned int length; unsigned char *msg = message_recv(conn, &segment, &length); unsigned char *reply = NULL; unsigned char *content = msg + FS_HEADER; if (!msg) { /* if the connection is in fact closed, this won't matter, but otherwise this error might help */ reply = fsp_error_new(segment, "protocol mismatch"); unsigned int* const l = (unsigned int *) (reply + 4); unsigned int length = *l; if (write(conn, reply, FS_HEADER + length) != (FS_HEADER+length)) { fs_error(LOG_ERR, "write failed: %s", strerror(errno)); } break; } if (auth) { switch (msg[3]) { case FS_NO_OP: reply = fsp_handle_no_op(segment, length, content); break; case FS_RESOLVE: reply = handle(backend->resolve, be, segment, length, content); break; case FS_BIND: reply = handle(backend->bind, be, segment, length, content); break; case FS_PRICE_BIND: reply = handle(backend->price, be, segment, length, content); break; case FS_DELETE_MODEL: reply = handle(backend->delete_models, be, segment, length, content); break; case FS_INSERT_RESOURCE: reply = handle(backend->insert_resource, be, segment, length, content); break; case FS_SEGMENTS: reply = handle(backend->segments, be, segment, length, content); break; case FS_COMMIT_RESOURCE: reply = handle(backend->commit_resource, be, segment, length, content); break; case FS_START_IMPORT: reply = handle(backend->start_import, be, segment, length, content); break; case FS_STOP_IMPORT: reply = handle(backend->stop_import, be, segment, length, content); break; case FS_GET_SIZE: reply = handle(backend->get_data_size, be, segment, length, content); break; case FS_GET_IMPORT_TIMES: reply = handle(backend->get_import_times, be, segment, length, content); break; case FS_INSERT_QUAD: reply = handle(backend->insert_quad, be, segment, length, content); break; case FS_COMMIT_QUAD: reply = handle(backend->commit_quad, be, segment, length, content); break; case FS_GET_QUERY_TIMES: reply = handle(backend->get_query_times, be, segment, length, content); break; case FS_BIND_LIMIT: reply = handle(backend->bind_limit, be, segment, length, content); break; case FS_BNODE_ALLOC: reply = handle(backend->bnode_alloc, be, segment, length, content); break; case FS_RESOLVE_ATTR: reply = handle(backend->resolve_attr, be, segment, length, content); break; case FS_DELETE_MODELS: reply = handle(backend->delete_models, be, segment, length, content); break; case FS_NEW_MODELS: reply = handle(backend->new_models, be, segment, length, content); break; case FS_BIND_FIRST: reply = handle(backend->bind_first, be, segment, length, content); break; case FS_BIND_NEXT: reply = handle(backend->bind_next, be, segment, length, content); break; case FS_BIND_DONE: reply = handle(backend->bind_done, be, segment, length, content); break; case FS_TRANSACTION: reply = handle(backend->transaction, be, segment, length, content); break; case FS_NODE_SEGMENTS: reply = handle(backend->node_segments, be, segment, length, content); break; case FS_REVERSE_BIND: reply = handle(backend->reverse_bind, be, segment, length, content); break; case FS_LOCK: reply = handle(backend->lock, be, segment, length, content); break; case FS_UNLOCK: reply = handle(backend->unlock, be, segment, length, content); break; case FS_GET_SIZE_REVERSE: reply = handle(backend->get_size_reverse, be, segment, length, content); break; case FS_GET_QUAD_FREQ: reply = handle(backend->get_quad_freq, be, segment, length, content); break; case FS_CHOOSE_SEGMENT: reply = handle(backend->choose_segment, be, segment, length, content); break; case FS_DELETE_QUADS: reply = handle(backend->delete_quads, be, segment, length, content); break; case FS_GET_UUID: reply = handle(backend->get_uuid, be, segment, length, content); break; default: kb_error(LOG_WARNING, "unexpected message type (%d)", msg[3]); reply = fsp_error_new(segment, "unexpected message type"); break; } } else if (msg[3] == FS_AUTH) { if (backend->auth) { reply = backend->auth(be, segment, length, content); } else { reply = message_new(FS_DONE_OK, segment, 0); } if (reply[3] == FS_DONE_OK) auth = 1; } else { reply = fsp_error_new(segment, "authenticate before continuing"); } if (reply) { unsigned int* const l = (unsigned int *) (reply + 4); unsigned int length = *l; if (write(conn, reply, FS_HEADER + length) <= 0) { kb_error(LOG_WARNING, "write reply failed"); } free(reply); } free(msg); } }
static unsigned char * handle_bind_first (fs_backend *be, fs_segment segment, unsigned int length, unsigned char *content) { unsigned char *reply; if (segment > be->segments) { fs_error(LOG_ERR, "invalid segment number: %d", segment); return fsp_error_new(segment, "invalid segment number"); } if (length < 32) { fs_error(LOG_ERR, "bind_first(%d) much too short", segment); return fsp_error_new(segment, "much too short"); } fs_rid_vector models, subjects, predicates, objects; unsigned int flags, value; int count; memcpy(&flags, content, sizeof (flags)); memcpy(&count, content + 4, sizeof (count)); memcpy(&value, content + 12, sizeof (models.length)); models.size = models.length = value / 8; memcpy(&value, content + 16, sizeof (subjects.length)); subjects.size = subjects.length = value / 8; memcpy(&value, content + 20, sizeof (predicates.length)); predicates.size = predicates.length = value / 8; memcpy(&value, content + 24, sizeof (objects.length)); objects.size = objects.length = value / 8; content += 32; if (length < (models.size + subjects.size + predicates.size + objects.size) * 8 + 32) { fs_error(LOG_ERR, "bind_first(%d) too short", segment); return fsp_error_new(segment, "too short"); } models.data = (fs_rid *) content; content += models.length * 8; subjects.data = (fs_rid *) content; content += subjects.length * 8; predicates.data = (fs_rid *) content; content += predicates.length * 8; objects.data = (fs_rid *) content; fs_rid_vector **bindings; bindings = fs_bind_first(be, segment, flags, &models, &subjects, &predicates, &objects, count); int k, cols = 0; for (k = 0; k < 4; ++k) { if (flags & 1 << k) cols++; } if (bindings == NULL) { /* NULL => no match */ reply = message_new(FS_NO_MATCH, segment, 0); cols = 0; } else if (cols == 0) { /* Zero columns => match with no binding */ reply = message_new(FS_BIND_LIST, segment, 0); } else { /* otherwise return bindings */ reply = message_new(FS_BIND_LIST, segment, bindings[0]->length * 8 * cols); unsigned char *data = reply + FS_HEADER; for (k= 0; k < cols; ++k) { memcpy(data, bindings[k]->data, bindings[k]->length * 8); data += bindings[k]->length * 8; } } for (k = 0; k < cols; ++k) { fs_rid_vector_free(bindings[k]); } free(bindings); return reply; }