Commit 975f280f authored by Michael Tremer's avatar Michael Tremer

database: Read all data from the large mmap()

Signed-off-by: default avatarMichael Tremer <michael.tremer@ipfire.org>
parent 6c27aec2
...@@ -142,35 +142,53 @@ struct loc_database_enumerator { ...@@ -142,35 +142,53 @@ struct loc_database_enumerator {
struct in6_addr gap4_start; struct in6_addr gap4_start;
}; };
#define loc_database_read_object(db, buffer, objects, pos) \
__loc_database_read_object(db, buffer, sizeof(*buffer), objects, pos)
/* /*
Reads an object into memory (used when mmap() isn't available) Checks if it is safe to read the buffer of size length starting at p.
*/ */
static void* __loc_database_read_object(struct loc_database* db, void* buffer, #define loc_database_check_boundaries(db, p) \
const size_t length, const struct loc_database_objects* objects, const off_t pos) { __loc_database_check_boundaries(db, (const char*)p, sizeof(*p))
// Calculate offset
const off_t offset = pos * length;
// Map the object first if possible static int __loc_database_check_boundaries(struct loc_database* db,
if (objects->data) const char* p, const size_t length) {
return objects->data + offset; size_t offset = p - db->data;
// Otherwise fall back and read the object into the buffer // Return if everything is within the boundary
const int fd = fileno(db->f); if (offset <= db->length - length)
return 1;
// Read object DEBUG(db->ctx, "Database read check failed at %p for %zu byte(s)\n", p, length);
ssize_t bytes_read = pread(fd, buffer, length, objects->offset + offset); DEBUG(db->ctx, " p = %p (offset = %jd, length = %zu)\n", p, offset, length);
DEBUG(db->ctx, " data = %p (length = %zu)\n", db->data, db->length);
DEBUG(db->ctx, " end = %p\n", db->data + db->length);
DEBUG(db->ctx, " overflow of %zu byte(s)\n", offset + length - db->length);
// Check if we could read what we wanted // Otherwise raise EFAULT
if (bytes_read < (ssize_t)length) { errno = EFAULT;
ERROR(db->ctx, "Error reading object from database: %m\n"); return 0;
}
/*
Returns a pointer to the n-th object
*/
static char* loc_database_object(struct loc_database* db,
const struct loc_database_objects* objects, const size_t length, const off_t n) {
// Return NULL if objects were not initialized
if (!objects->data) {
errno = EFAULT;
return NULL; return NULL;
} }
// Success! // Calculate offset
return buffer; const off_t offset = n * length;
// Return a pointer to where the object lies
char* object = objects->data + offset;
// Check if the object is part of the memory
if (!__loc_database_check_boundaries(db, object, length))
return NULL;
return object;
} }
static int loc_database_version_supported(struct loc_database* db, uint8_t version) { static int loc_database_version_supported(struct loc_database* db, uint8_t version) {
...@@ -285,25 +303,6 @@ static int loc_database_read_signature(struct loc_database* db, ...@@ -285,25 +303,6 @@ static int loc_database_read_signature(struct loc_database* db,
return 0; return 0;
} }
/*
Checks if it is safe to read the buffer of size length starting at p.
*/
#define loc_database_check_boundaries(db, p) \
__loc_database_check_boundaries(db, (const char*)p, sizeof(*p))
static int __loc_database_check_boundaries(struct loc_database* db,
const char* p, const size_t length) {
size_t offset = p - db->data;
// Return if everything is within the boundary
if (offset < db->length - length)
return 1;
// Otherwise raise EFAULT
errno = EFAULT;
return 0;
}
static int loc_database_read_header_v1(struct loc_database* db) { static int loc_database_read_header_v1(struct loc_database* db) {
const struct loc_database_header_v1* header = const struct loc_database_header_v1* header =
(const struct loc_database_header_v1*)(db->data + LOC_DATABASE_MAGIC_SIZE); (const struct loc_database_header_v1*)(db->data + LOC_DATABASE_MAGIC_SIZE);
...@@ -337,9 +336,15 @@ static int loc_database_read_header_v1(struct loc_database* db) { ...@@ -337,9 +336,15 @@ static int loc_database_read_header_v1(struct loc_database* db) {
if (r) if (r)
return r; return r;
const char* stringpool_start = db->data + be32toh(header->pool_offset);
size_t stringpool_length = be32toh(header->pool_length);
// Check if the stringpool is part of the mapped area
if (!__loc_database_check_boundaries(db, stringpool_start, stringpool_length))
return 1;
// Open the stringpool // Open the stringpool
r = loc_stringpool_open(db->ctx, &db->pool, db->f, r = loc_stringpool_open(db->ctx, &db->pool, stringpool_start, stringpool_length);
be32toh(header->pool_length), be32toh(header->pool_offset));
if (r) if (r)
return r; return r;
...@@ -709,8 +714,7 @@ LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) { ...@@ -709,8 +714,7 @@ LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) {
// Returns the AS at position pos // Returns the AS at position pos
static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) { static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) {
struct loc_database_as_v1 as_v1; struct loc_database_as_v1* as_v1 = NULL;
struct loc_database_as_v1* p_v1;
int r; int r;
if ((size_t)pos >= db->as_objects.count) { if ((size_t)pos >= db->as_objects.count) {
...@@ -722,12 +726,13 @@ static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, of ...@@ -722,12 +726,13 @@ static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, of
switch (db->version) { switch (db->version) {
case LOC_DATABASE_VERSION_1: case LOC_DATABASE_VERSION_1:
// Read the object // Find the object
p_v1 = loc_database_read_object(db, &as_v1, &db->as_objects, pos); as_v1 = (struct loc_database_as_v1*)loc_database_object(db,
if (!p_v1) &db->as_objects, sizeof(*as_v1), pos);
if (!as_v1)
return 1; return 1;
r = loc_as_new_from_database_v1(db->ctx, db->pool, as, p_v1); r = loc_as_new_from_database_v1(db->ctx, db->pool, as, as_v1);
break; break;
default: default:
...@@ -792,8 +797,7 @@ LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, ...@@ -792,8 +797,7 @@ LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as,
// Returns the network at position pos // Returns the network at position pos
static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network, static int loc_database_fetch_network(struct loc_database* db, struct loc_network** network,
struct in6_addr* address, unsigned int prefix, off_t pos) { struct in6_addr* address, unsigned int prefix, off_t pos) {
struct loc_database_network_v1 network_v1; struct loc_database_network_v1* network_v1 = NULL;
struct loc_database_network_v1* p_v1;
int r; int r;
if ((size_t)pos >= db->network_objects.count) { if ((size_t)pos >= db->network_objects.count) {
...@@ -808,11 +812,12 @@ static int loc_database_fetch_network(struct loc_database* db, struct loc_networ ...@@ -808,11 +812,12 @@ static int loc_database_fetch_network(struct loc_database* db, struct loc_networ
switch (db->version) { switch (db->version) {
case LOC_DATABASE_VERSION_1: case LOC_DATABASE_VERSION_1:
// Read the object // Read the object
p_v1 = loc_database_read_object(db, &network_v1, &db->network_objects, pos); network_v1 = (struct loc_database_network_v1*)loc_database_object(db,
if (!p_v1) &db->network_objects, sizeof(*network_v1), pos);
if (!network_v1)
return 1; return 1;
r = loc_network_new_from_database_v1(db->ctx, network, address, prefix, p_v1); r = loc_network_new_from_database_v1(db->ctx, network, address, prefix, network_v1);
break; break;
default: default:
...@@ -862,14 +867,14 @@ static int __loc_database_lookup_handle_leaf(struct loc_database* db, const stru ...@@ -862,14 +867,14 @@ static int __loc_database_lookup_handle_leaf(struct loc_database* db, const stru
static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address, static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
struct loc_network** network, struct in6_addr* network_address, struct loc_network** network, struct in6_addr* network_address,
off_t node_index, unsigned int level) { off_t node_index, unsigned int level) {
struct loc_database_network_node_v1 node_v1; struct loc_database_network_node_v1* node_v1 = NULL;
struct loc_database_network_node_v1* p_v1;
int r; int r;
// Fetch the next node // Fetch the next node
p_v1 = loc_database_read_object(db, &node_v1, &db->network_node_objects, node_index); node_v1 = (struct loc_database_network_node_v1*)loc_database_object(db,
if (!p_v1) &db->network_node_objects, sizeof(*node_v1), node_index);
if (!node_v1)
return 1; return 1;
// Follow the path // Follow the path
...@@ -877,9 +882,9 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* ...@@ -877,9 +882,9 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr*
loc_address_set_bit(network_address, level, bit); loc_address_set_bit(network_address, level, bit);
if (bit == 0) if (bit == 0)
node_index = be32toh(p_v1->zero); node_index = be32toh(node_v1->zero);
else else
node_index = be32toh(p_v1->one); node_index = be32toh(node_v1->one);
// If the node index is zero, the tree ends here // If the node index is zero, the tree ends here
// and we cannot descend any further // and we cannot descend any further
...@@ -907,8 +912,8 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* ...@@ -907,8 +912,8 @@ static int __loc_database_lookup(struct loc_database* db, const struct in6_addr*
} }
// If this node has a leaf, we will check if it matches // If this node has a leaf, we will check if it matches
if (__loc_database_node_is_leaf(p_v1)) { if (__loc_database_node_is_leaf(node_v1)) {
r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, p_v1); r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node_v1);
if (r <= 0) if (r <= 0)
return r; return r;
} }
...@@ -955,8 +960,7 @@ LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db, ...@@ -955,8 +960,7 @@ LOC_EXPORT int loc_database_lookup_from_string(struct loc_database* db,
// Returns the country at position pos // Returns the country at position pos
static int loc_database_fetch_country(struct loc_database* db, static int loc_database_fetch_country(struct loc_database* db,
struct loc_country** country, off_t pos) { struct loc_country** country, off_t pos) {
struct loc_database_country_v1 country_v1; struct loc_database_country_v1* country_v1 = NULL;
struct loc_database_country_v1* p_v1;
int r; int r;
// Check if the country is within range // Check if the country is within range
...@@ -970,11 +974,12 @@ static int loc_database_fetch_country(struct loc_database* db, ...@@ -970,11 +974,12 @@ static int loc_database_fetch_country(struct loc_database* db,
switch (db->version) { switch (db->version) {
case LOC_DATABASE_VERSION_1: case LOC_DATABASE_VERSION_1:
// Read the object // Read the object
p_v1 = loc_database_read_object(db, &country_v1, &db->country_objects, pos); country_v1 = (struct loc_database_country_v1*)loc_database_object(db,
if (!p_v1) &db->country_objects, sizeof(*country_v1), pos);
if (!country_v1)
return 1; return 1;
r = loc_country_new_from_database_v1(db->ctx, db->pool, country, p_v1); r = loc_country_new_from_database_v1(db->ctx, db->pool, country, country_v1);
break; break;
default: default:
...@@ -1315,8 +1320,6 @@ static int loc_database_enumerator_match_network( ...@@ -1315,8 +1320,6 @@ static int loc_database_enumerator_match_network(
static int __loc_database_enumerator_next_network( static int __loc_database_enumerator_next_network(
struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) { struct loc_database_enumerator* enumerator, struct loc_network** network, int filter) {
struct loc_database_network_node_v1 node_v1;
// Return top element from the stack // Return top element from the stack
while (1) { while (1) {
*network = loc_network_list_pop_first(enumerator->stack); *network = loc_network_list_pop_first(enumerator->stack);
...@@ -1360,8 +1363,9 @@ static int __loc_database_enumerator_next_network( ...@@ -1360,8 +1363,9 @@ static int __loc_database_enumerator_next_network(
enumerator->networks_visited[node->offset]++; enumerator->networks_visited[node->offset]++;
// Pop node from top of the stack // Pop node from top of the stack
struct loc_database_network_node_v1* n = loc_database_read_object(enumerator->db, struct loc_database_network_node_v1* n =
&node_v1, &enumerator->db->network_node_objects, node->offset); (struct loc_database_network_node_v1*)loc_database_object(enumerator->db,
&enumerator->db->network_node_objects, sizeof(*n), node->offset);
if (!n) if (!n)
return 1; return 1;
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
struct loc_stringpool; struct loc_stringpool;
int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool); int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool);
int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool, int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
FILE* f, size_t length, off_t offset); const char* data, const size_t length);
struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool); struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool);
struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool); struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool);
......
...@@ -27,89 +27,36 @@ ...@@ -27,89 +27,36 @@
#include <libloc/private.h> #include <libloc/private.h>
#include <libloc/stringpool.h> #include <libloc/stringpool.h>
#define LOC_STRINGPOOL_BLOCK_SIZE (512 * 1024)
struct loc_stringpool { struct loc_stringpool {
struct loc_ctx* ctx; struct loc_ctx* ctx;
int refcount; int refcount;
// A file descriptor when we open an existing stringpool // Reference to any mapped data
int fd; const char* data;
off_t offset;
ssize_t length; ssize_t length;
// Mapped data (from mmap()) // Reference to own storage
char* mmapped_data; char* blocks;
size_t size;
char* data;
char* pos;
char buffer[LOC_DATABASE_PAGE_SIZE];
}; };
static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) { static int loc_stringpool_grow(struct loc_stringpool* pool, const size_t size) {
if (pos < pool->data) { DEBUG(pool->ctx, "Growing string pool by %zu byte(s)\n", size);
errno = EFAULT;
return -1;
}
if (pos > (pool->data + pool->length)) {
errno = EFAULT;
return -1;
}
return pos - pool->data; // Increment size
} pool->size += size;
static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
ssize_t bytes_read;
// Check boundaries
if (offset < 0 || offset >= pool->length) {
errno = ERANGE;
return NULL;
}
// Return any data that we have in memory // Reallocate blocks
if (pool->data) pool->blocks = realloc(pool->blocks, pool->size);
return pool->data + offset; if (!pool->blocks) {
ERROR(pool->ctx, "Could not grow string pool: %m\n");
// Otherwise read a block from file
bytes_read = pread(pool->fd, pool->buffer, sizeof(pool->buffer),
pool->offset + offset);
// Break on error
if (bytes_read < 0) {
ERROR(pool->ctx, "Could not read from string pool: %m\n");
return NULL;
}
// It is okay, if we did not read as much as we wanted, since we might be reading
// the last block which might be of an unknown size.
// Search for a complete string. If there is no NULL byte, the block is garbage.
char* end = memchr(pool->buffer, bytes_read, '\0');
if (!end)
return NULL;
// Return what's in the buffer
return pool->buffer;
}
static int loc_stringpool_grow(struct loc_stringpool* pool, size_t length) {
DEBUG(pool->ctx, "Growing string pool to %zu bytes\n", length);
// Save pos pointer
off_t pos = loc_stringpool_get_offset(pool, pool->pos);
// Reallocate data section
pool->data = realloc(pool->data, length);
if (!pool->data)
return 1; return 1;
}
pool->length = length; // Update data pointer
pool->data = pool->blocks;
// Restore pos
pool->pos = __loc_stringpool_get(pool, pos);
return 0; return 0;
} }
...@@ -122,44 +69,33 @@ static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* stri ...@@ -122,44 +69,33 @@ static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* stri
DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool); DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
// How much space to we need?
const size_t length = strlen(string) + 1;
// Make sure we have enough space // Make sure we have enough space
int r = loc_stringpool_grow(pool, pool->length + strlen(string) + 1); if (pool->length + length > pool->size) {
if (r) int r = loc_stringpool_grow(pool, LOC_STRINGPOOL_BLOCK_SIZE);
return -1; if (r)
return r;
}
off_t offset = loc_stringpool_get_offset(pool, pool->pos); off_t offset = pool->length;
// Copy string byte by byte // Copy the string
while (*string) memcpy(pool->blocks + offset, string, length);
*pool->pos++ = *string++;
// Terminate the string // Update the length of the pool
*pool->pos++ = '\0'; pool->length += length;
return offset; return offset;
} }
static void loc_stringpool_free(struct loc_stringpool* pool) { static void loc_stringpool_free(struct loc_stringpool* pool) {
DEBUG(pool->ctx, "Releasing string pool %p\n", pool); DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
int r;
// Close file
if (pool->fd > 0)
close(pool->fd);
// Unmap any mapped memory
if (pool->mmapped_data) {
r = munmap(pool->mmapped_data, pool->length);
if (r)
ERROR(pool->ctx, "Error unmapping string pool: %m\n");
if (pool->mmapped_data == pool->data)
pool->data = NULL;
}
// Free any data // Free any data
if (pool->data) if (pool->blocks)
free(pool->data); free(pool->blocks);
loc_unref(pool->ctx); loc_unref(pool->ctx);
free(pool); free(pool);
...@@ -178,27 +114,8 @@ int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) { ...@@ -178,27 +114,8 @@ int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) {
return 0; return 0;
} }
static int loc_stringpool_mmap(struct loc_stringpool* pool) {
// Try mmap()
char* p = mmap(NULL, pool->length, PROT_READ, MAP_PRIVATE, pool->fd, pool->offset);
if (p == MAP_FAILED) {
// Ignore if data hasn't been aligned correctly
if (errno == EINVAL)
return 0;
ERROR(pool->ctx, "Could not mmap stringpool: %m\n");
return 1;
}
// Store mapped memory area
pool->data = pool->mmapped_data = pool->pos = p;
return 0;
}
int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool, int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
FILE* f, size_t length, off_t offset) { const char* data, const size_t length) {
struct loc_stringpool* p = NULL; struct loc_stringpool* p = NULL;
// Allocate a new stringpool // Allocate a new stringpool
...@@ -206,29 +123,11 @@ int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool, ...@@ -206,29 +123,11 @@ int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
if (r) if (r)
goto ERROR; goto ERROR;
// Store offset and length // Store data and length
p->offset = offset; p->data = data;
p->length = length; p->length = length;
DEBUG(p->ctx, "Reading string pool starting from %jd (%zu bytes)\n", DEBUG(p->ctx, "Opened string pool at %p (%zu bytes)\n", p->data, p->length);
(intmax_t)p->offset, p->length);
int fd = fileno(f);
// Copy the file descriptor
p->fd = dup(fd);
if (p->fd < 0) {
ERROR(ctx, "Could not duplicate file the file descriptor: %m\n");
r = 1;
goto ERROR;
}
// Map data into memory
if (p->length > 0) {
r = loc_stringpool_mmap(p);
if (r)
goto ERROR;
}
*pool = p; *pool = p;
return 0; return 0;
...@@ -256,11 +155,18 @@ struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) { ...@@ -256,11 +155,18 @@ struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
} }
const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) { const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
return __loc_stringpool_get(pool, offset); // Check boundaries
if (offset < 0 || offset >= pool->length) {
errno = ERANGE;
return NULL;
}
// Return any data that we have in memory
return pool->data + offset;
} }
size_t loc_stringpool_get_size(struct loc_stringpool* pool) { size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
return loc_stringpool_get_offset(pool, pool->pos); return pool->length;
} }
static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) { static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment