#include "gfdb_data_store_helper.h" #include "syscall.h" /****************************************************************************** * * Query record related functions * * ****************************************************************************/ /*Create a single link info structure*/ gfdb_link_info_t * gfdb_link_info_new() { gfdb_link_info_t *link_info = NULL; link_info = GF_CALLOC(1, sizeof(gfdb_link_info_t), gf_mt_gfdb_link_info_t); if (!link_info) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY, "Memory allocation failed for " "link_info "); goto out; } INIT_LIST_HEAD(&link_info->list); out: return link_info; } /*Destroy a link info structure*/ void gfdb_link_info_free(gfdb_link_info_t *link_info) { GF_FREE(link_info); } /*Function to create the query_record*/ gfdb_query_record_t * gfdb_query_record_new() { int ret = -1; gfdb_query_record_t *query_record = NULL; query_record = GF_CALLOC(1, sizeof(gfdb_query_record_t), gf_mt_gfdb_query_record_t); if (!query_record) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, ENOMEM, LG_MSG_NO_MEMORY, "Memory allocation failed for " "query_record "); goto out; } INIT_LIST_HEAD(&query_record->link_list); ret = 0; out: if (ret == -1) { GF_FREE(query_record); } return query_record; } /*Function to delete a single linkinfo from list*/ static void gfdb_delete_linkinfo_from_list(gfdb_link_info_t **link_info) { GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, link_info, out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, *link_info, out); /*Remove hard link from list*/ list_del(&(*link_info)->list); gfdb_link_info_free(*link_info); link_info = NULL; out: return; } /*Function to destroy link_info list*/ void gfdb_free_link_info_list(gfdb_query_record_t *query_record) { gfdb_link_info_t *link_info = NULL; gfdb_link_info_t *temp = NULL; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); list_for_each_entry_safe(link_info, temp, &query_record->link_list, list) { gfdb_delete_linkinfo_from_list(&link_info); link_info = NULL; } out: return; } /* Function to add linkinfo to the query record */ int gfdb_add_link_to_query_record(gfdb_query_record_t *query_record, uuid_t pgfid, char *base_name) { int ret = -1; gfdb_link_info_t *link_info = NULL; int base_name_len = 0; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, pgfid, out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, base_name, out); link_info = gfdb_link_info_new(); if (!link_info) { goto out; } gf_uuid_copy(link_info->pargfid, pgfid); base_name_len = strlen(base_name); memcpy(link_info->file_name, base_name, base_name_len); link_info->file_name[base_name_len] = '\0'; list_add_tail(&link_info->list, &query_record->link_list); query_record->link_count++; ret = 0; out: if (ret) { gfdb_link_info_free(link_info); link_info = NULL; } return ret; } /*Function to destroy query record*/ void gfdb_query_record_free(gfdb_query_record_t *query_record) { if (query_record) { gfdb_free_link_info_list(query_record); GF_FREE(query_record); } } /****************************************************************************** SERIALIZATION/DE-SERIALIZATION OF QUERY RECORD *******************************************************************************/ /****************************************************************************** The on disk format of query record is as follows, +---------------------------------------------------------------------------+ | Length of serialized query record | Serialized Query Record | +---------------------------------------------------------------------------+ 4 bytes Length of serialized query record | | -------------------------------------------------| | | V Serialized Query Record Format: +---------------------------------------------------------------------------+ | GFID | Link count | |..... | FOOTER | +---------------------------------------------------------------------------+ 16 B 4 B Link Length 4 B | | | | -----------------------------| | | | | | V | Each will be serialized as | +-----------------------------------------------+ | | PGID | BASE_NAME_LENGTH | BASE_NAME | | +-----------------------------------------------+ | 16 B 4 B BASE_NAME_LENGTH | | | ------------------------------------------------------------------------| | | V FOOTER is a magic number 0xBAADF00D indicating the end of the record. This also serves as a serialized schema validator. * ****************************************************************************/ #define GFDB_QUERY_RECORD_FOOTER 0xBAADF00D #define UUID_LEN 16 /*Function to get the potential length of the serialized buffer*/ static int32_t gfdb_query_record_serialized_length(gfdb_query_record_t *query_record) { int32_t len = -1; gfdb_link_info_t *link_info = NULL; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); /* Length of GFID */ len = UUID_LEN; /* length of number of links*/ len += sizeof(int32_t); list_for_each_entry(link_info, &query_record->link_list, list) { /* length of PFID */ len += UUID_LEN; /* Add size of base name length*/ len += sizeof(int32_t); /* Length of base_name */ len += strlen(link_info->file_name); } /* length of footer */ len += sizeof(int32_t); out: return len; } /* Function for serializing query record. * * Query Record Serialization Format * +---------------------------------------------------------------------------+ * | GFID | Link count | |..... | FOOTER | * +---------------------------------------------------------------------------+ * 16 B 4 B Link Length 4 B * * * Each will be serialized as * +-----------------------------------------------+ * | PGID | BASE_NAME_LENGTH | BASE_NAME | * +-----------------------------------------------+ * 16 B 4 B BASE_NAME_LENGTH * * * FOOTER is a magic number 0xBAADF00D indicating the end of the record. * This also serves as a serialized schema validator. * * The function will allocate memory to the serialized buffer, * the caller needs to free it. * Returns the length of the serialized buffer on success * or -1 on failure. * * */ static int gfdb_query_record_serialize(gfdb_query_record_t *query_record, char **in_buffer) { gfdb_link_info_t *link_info = NULL; int count = -1; int base_name_len = 0; int buffer_length = 0; int footer = GFDB_QUERY_RECORD_FOOTER; char *buffer = NULL; char *ret_buffer = NULL; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, (query_record->link_count > 0), out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, in_buffer, out); /* Calculate the total length of the serialized buffer */ buffer_length = gfdb_query_record_serialized_length(query_record); if (buffer_length <= 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to calculate the length of " "serialized buffer"); goto out; } /* Allocate memory to the serialized buffer */ ret_buffer = GF_CALLOC(1, buffer_length, gf_common_mt_char); if (!ret_buffer) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Memory allocation failed for " "serialized buffer."); goto out; } buffer = ret_buffer; count = 0; /* Copying the GFID */ memcpy(buffer, query_record->gfid, UUID_LEN); buffer += UUID_LEN; count += UUID_LEN; /* Copying the number of links */ memcpy(buffer, &query_record->link_count, sizeof(int32_t)); buffer += sizeof(int32_t); count += sizeof(int32_t); list_for_each_entry(link_info, &query_record->link_list, list) { /* Copying the PFID */ memcpy(buffer, link_info->pargfid, UUID_LEN); buffer += UUID_LEN; count += UUID_LEN; /* Copying base name length*/ base_name_len = strlen(link_info->file_name); memcpy(buffer, &base_name_len, sizeof(int32_t)); buffer += sizeof(int32_t); count += sizeof(int32_t); /* Length of base_name */ memcpy(buffer, link_info->file_name, base_name_len); buffer += base_name_len; count += base_name_len; } /* Copying the Footer of the record */ memcpy(buffer, &footer, sizeof(int32_t)); count += sizeof(int32_t); out: if (count < 0) { GF_FREE(ret_buffer); ret_buffer = NULL; } *in_buffer = ret_buffer; return count; } static gf_boolean_t is_serialized_buffer_valid(char *in_buffer, int buffer_length) { gf_boolean_t ret = _gf_false; int footer = 0; /* Read the footer */ in_buffer += (buffer_length - sizeof(int32_t)); memcpy(&footer, in_buffer, sizeof(int32_t)); /* * if the footer is not GFDB_QUERY_RECORD_FOOTER * then the serialized record is invalid * * */ if (footer != GFDB_QUERY_RECORD_FOOTER) { goto out; } ret = _gf_true; out: return ret; } static int gfdb_query_record_deserialize(char *in_buffer, int buffer_length, gfdb_query_record_t **query_record) { int ret = -1; char *buffer = NULL; int i = 0; gfdb_link_info_t *link_info = NULL; int count = 0; int base_name_len = 0; gfdb_query_record_t *ret_qrecord = NULL; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, in_buffer, out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, (buffer_length > 0), out); if (!is_serialized_buffer_valid(in_buffer, buffer_length)) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Invalid serialized query record"); goto out; } buffer = in_buffer; ret_qrecord = gfdb_query_record_new(); if (!ret_qrecord) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to allocate space to " "gfdb_query_record_t"); goto out; } /* READ GFID */ memcpy((ret_qrecord)->gfid, buffer, UUID_LEN); buffer += UUID_LEN; count += UUID_LEN; /* Read the number of link */ memcpy(&(ret_qrecord->link_count), buffer, sizeof(int32_t)); buffer += sizeof(int32_t); count += sizeof(int32_t); /* Read all the links */ for (i = 0; i < ret_qrecord->link_count; i++) { if (count >= buffer_length) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Invalid serialized " "query record"); ret = -1; goto out; } link_info = gfdb_link_info_new(); if (!link_info) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to create link_info"); goto out; } /* READ PGFID */ memcpy(link_info->pargfid, buffer, UUID_LEN); buffer += UUID_LEN; count += UUID_LEN; /* Read base name length */ memcpy(&base_name_len, buffer, sizeof(int32_t)); buffer += sizeof(int32_t); count += sizeof(int32_t); /* READ basename */ memcpy(link_info->file_name, buffer, base_name_len); buffer += base_name_len; count += base_name_len; link_info->file_name[base_name_len] = '\0'; /* Add link_info to the list */ list_add_tail(&link_info->list, &(ret_qrecord->link_list)); /* Resetting link_info */ link_info = NULL; } ret = 0; out: if (ret) { gfdb_query_record_free(ret_qrecord); ret_qrecord = NULL; } *query_record = ret_qrecord; return ret; } /* Function to write query record to file * * Disk format * +---------------------------------------------------------------------------+ * | Length of serialized query record | Serialized Query Record | * +---------------------------------------------------------------------------+ * 4 bytes Length of serialized query record * * Please refer gfdb_query_record_serialize () for format of * Serialized Query Record * * */ int gfdb_write_query_record(int fd, gfdb_query_record_t *query_record) { int ret = -1; int buffer_len = 0; char *buffer = NULL; int write_len = 0; char *write_buffer = NULL; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, (fd >= 0), out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); buffer_len = gfdb_query_record_serialize(query_record, &buffer); if (buffer_len < 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to serialize query record"); goto out; } /* Serialize the buffer length and write to file */ ret = write(fd, &buffer_len, sizeof(int32_t)); if (ret < 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to write buffer length" " to file"); goto out; } /* Write the serialized query record to file */ write_len = buffer_len; write_buffer = buffer; while ((ret = write(fd, write_buffer, write_len)) < write_len) { if (ret < 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, errno, LG_MSG_DB_ERROR, "Failed to write serialized " "query record to file"); goto out; } write_buffer += ret; write_len -= ret; } ret = 0; out: GF_FREE(buffer); return ret; } /* Function to read query record from file. * Allocates memory to query record and * returns length of serialized query record when successful * Return -1 when failed. * Return 0 when reached EOF. * */ int gfdb_read_query_record(int fd, gfdb_query_record_t **query_record) { int ret = -1; int buffer_len = 0; int read_len = 0; char *buffer = NULL; char *read_buffer = NULL; GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, (fd >= 0), out); GF_VALIDATE_OR_GOTO(GFDB_DATA_STORE, query_record, out); /* Read serialized query record length from the file*/ ret = sys_read(fd, &buffer_len, sizeof(int32_t)); if (ret < 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed reading buffer length" " from file"); goto out; } /* EOF */ else if (ret == 0) { ret = 0; goto out; } /* Assumed sane range is 1B - 10MB */ if ((buffer_len <= 0) || (buffer_len > (10 * 1024 * 1024))) { ret = -1; gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "buffer length range is out of bound %d", buffer_len); goto out; } /* Allocating memory to the serialization buffer */ buffer = GF_CALLOC(1, buffer_len, gf_common_mt_char); if (!buffer) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to allocate space to " "serialized buffer"); goto out; } /* Read the serialized query record from file */ read_len = buffer_len; read_buffer = buffer; while ((ret = sys_read(fd, read_buffer, read_len)) < read_len) { /*Any error */ if (ret < 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, errno, LG_MSG_DB_ERROR, "Failed to read serialized " "query record from file"); goto out; } /* EOF */ else if (ret == 0) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Invalid query record or " "corrupted query file"); ret = -1; goto out; } read_buffer += ret; read_len -= ret; } ret = gfdb_query_record_deserialize(buffer, buffer_len, query_record); if (ret) { gf_msg(GFDB_DATA_STORE, GF_LOG_ERROR, 0, LG_MSG_DB_ERROR, "Failed to de-serialize query record"); goto out; } ret = buffer_len; out: GF_FREE(buffer); return ret; }