/* easier to use string based interface */ gchar * persist_state_lookup_string(PersistState *self, const gchar *key, gsize *length, guint8 *version) { PersistEntryHandle handle; gpointer block; SerializeArchive *sa; gchar *result; gsize result_len, size; guint8 result_version; gboolean success; if (!(handle = persist_state_lookup_entry(self, key, &size, &result_version))) return NULL; block = persist_state_map_entry(self, handle); sa = serialize_buffer_archive_new(block, size); success = serialize_read_cstring(sa, &result, &result_len); serialize_archive_free(sa); persist_state_unmap_entry(self, handle); if (!success) return NULL; if (length) *length = result_len; if (version) *version = result_version; return result; }
/* function to load v2 and v3 format persistent files */ static gboolean _load_v23(PersistState *self, gint version, SerializeArchive *sa) { gchar *key, *value; while (serialize_read_cstring(sa, &key, NULL)) { gsize len; guint32 str_len; if (key[0] && serialize_read_cstring(sa, &value, &len)) { gpointer new_block; PersistEntryHandle new_handle; /* add length of the string */ new_handle = _alloc_value(self, len + sizeof(str_len), FALSE, version); new_block = persist_state_map_entry(self, new_handle); /* NOTE: we add an extra length field to the old value, as our * persist_state_lookup_string() needs that. * persist_state_lookup_string is used to fetch disk queue file * names. It could have been solved somewhat better, but now it * doesn't justify a persist-state file format change. */ str_len = GUINT32_TO_BE(len); memcpy(new_block, &str_len, sizeof(str_len)); memcpy(new_block + sizeof(str_len), value, len); persist_state_unmap_entry(self, new_handle); /* add key to the current file */ _add_key(self, key, new_handle); g_free(value); g_free(key); } else { g_free(key); break; } } return TRUE; }
static gboolean log_proto_buffered_server_convert_state(LogProtoBufferedServer *self, guint8 persist_version, gpointer old_state, gsize old_state_size, LogProtoBufferedServerState *state) { if (persist_version <= 2) { state->header.version = 0; state->file_inode = 0; state->raw_stream_pos = strtoll((gchar *) old_state, NULL, 10); state->file_size = 0; return TRUE; } else if (persist_version == 3) { SerializeArchive *archive; guint32 read_length; gint64 cur_size; gint64 cur_inode; gint64 cur_pos; guint16 version; gchar *buffer; gsize buffer_len; cur_inode = -1; cur_pos = 0; cur_size = 0; archive = serialize_buffer_archive_new(old_state, old_state_size); /* NOTE: the v23 conversion code adds an extra length field which we * need to read out. */ g_assert(serialize_read_uint32(archive, &read_length) && read_length == old_state_size - sizeof(read_length)); /* original v3 format starts here */ if (!serialize_read_uint16(archive, &version) || version != 0) { msg_error("Internal error restoring log reader state, stored data has incorrect version", evt_tag_int("version", version)); goto error_converting_v3; } if (!serialize_read_uint64(archive, (guint64 *) &cur_pos) || !serialize_read_uint64(archive, (guint64 *) &cur_inode) || !serialize_read_uint64(archive, (guint64 *) &cur_size)) { msg_error("Internal error restoring information about the current file position, restarting from the beginning"); goto error_converting_v3; } if (!serialize_read_uint16(archive, &version) || version != 0) { msg_error("Internal error, protocol state has incorrect version", evt_tag_int("version", version)); goto error_converting_v3; } if (!serialize_read_cstring(archive, &buffer, &buffer_len)) { msg_error("Internal error, error reading buffer contents", evt_tag_int("version", version)); goto error_converting_v3; } if (!self->buffer || state->buffer_size < buffer_len) { gsize buffer_size = MAX(self->super.options->init_buffer_size, buffer_len); self->buffer = g_realloc(self->buffer, buffer_size); } serialize_archive_free(archive); memcpy(self->buffer, buffer, buffer_len); state->buffer_pos = 0; state->pending_buffer_end = buffer_len; g_free(buffer); state->header.version = 0; state->file_inode = cur_inode; state->raw_stream_pos = cur_pos; state->file_size = cur_size; return TRUE; error_converting_v3: serialize_archive_free(archive); } return FALSE; }
static gboolean _load_v4(PersistState *self, gboolean load_all_entries) { gint fd; gint64 file_size; gpointer map; gpointer key_block; guint32 key_size; PersistFileHeader *header; gint key_count, i; fd = open(self->commited_filename, O_RDONLY); if (fd < 0) { /* no previous data found */ return TRUE; } file_size = lseek(fd, 0, SEEK_END); if (file_size > ((1LL << 31) - 1)) { msg_error("Persistent file too large", evt_tag_str("filename", self->commited_filename), evt_tag_printf("size", "%" G_GINT64_FORMAT, file_size), NULL); return FALSE; } map = mmap(NULL, file_size, PROT_READ, MAP_SHARED, fd, 0); close(fd); if (map == MAP_FAILED) { msg_error("Error mapping persistent file into memory", evt_tag_str("filename", self->commited_filename), evt_tag_errno("error", errno), NULL); return FALSE; } header = (PersistFileHeader *) map; key_block = ((gchar *) map) + offsetof(PersistFileHeader, initial_key_store); key_size = sizeof((((PersistFileHeader *) NULL))->initial_key_store); key_count = GUINT32_FROM_BE(header->key_count); i = 0; while (i < key_count) { gchar *name; guint32 entry_ofs, chain_ofs; SerializeArchive *sa; sa = serialize_buffer_archive_new(key_block, key_size); while (i < key_count) { if (!serialize_read_cstring(sa, &name, NULL)) { serialize_archive_free(sa); msg_error("Persistent file format error, unable to fetch key name", NULL); goto free_and_exit; } if (name[0]) { if (serialize_read_uint32(sa, &entry_ofs)) { PersistValueHeader *value_header; i++; if (entry_ofs < sizeof(PersistFileHeader) || entry_ofs > file_size) { serialize_archive_free(sa); g_free(name); msg_error("Persistent file format error, entry offset is out of bounds", NULL); goto free_and_exit; } value_header = (PersistValueHeader *) ((gchar *) map + entry_ofs - sizeof(PersistValueHeader)); if ((value_header->in_use) || load_all_entries) { gpointer new_block; PersistEntryHandle new_handle; new_handle = _alloc_value(self, GUINT32_FROM_BE(value_header->size), FALSE, value_header->version); new_block = persist_state_map_entry(self, new_handle); memcpy(new_block, value_header + 1, GUINT32_FROM_BE(value_header->size)); persist_state_unmap_entry(self, new_handle); /* add key to the current file */ _add_key(self, name, new_handle); } g_free(name); } else { /* bad format */ serialize_archive_free(sa); g_free(name); msg_error("Persistent file format error, unable to fetch key name", NULL); goto free_and_exit; } } else { g_free(name); if (serialize_read_uint32(sa, &chain_ofs)) { /* end of block, chain to the next one */ if (chain_ofs == 0 || chain_ofs > file_size) { msg_error("Persistent file format error, key block chain offset is too large or zero", evt_tag_printf("key_block", "%08lx", (gulong) ((gchar *) key_block - (gchar *) map)), evt_tag_printf("key_size", "%d", key_size), evt_tag_int("ofs", chain_ofs), NULL); serialize_archive_free(sa); goto free_and_exit; } key_block = ((gchar *) map) + chain_ofs; key_size = GUINT32_FROM_BE(*(guint32 *) (((gchar *) key_block) - sizeof(PersistValueHeader))); if (chain_ofs + key_size > file_size) { msg_error("Persistent file format error, key block size is too large", evt_tag_int("key_size", key_size), NULL); serialize_archive_free(sa); goto free_and_exit; } break; } else { serialize_archive_free(sa); msg_error("Persistent file format error, unable to fetch chained key block offset", NULL); goto free_and_exit; } } } serialize_archive_free(sa); } free_and_exit: munmap(map, file_size); return TRUE; }