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,
- const size_t length, const struct loc_database_objects* objects, const off_t pos) {
- // Calculate offset
- const off_t offset = pos * length;
+#define loc_database_check_boundaries(db, p) \
+ __loc_database_check_boundaries(db, (const char*)p, sizeof(*p))
- // Map the object first if possible
- if (objects->data)
- return objects->data + offset;
+static int __loc_database_check_boundaries(struct loc_database* db,
+ const char* p, const size_t length) {
+ size_t offset = p - db->data;
- // Otherwise fall back and read the object into the buffer
- const int fd = fileno(db->f);
+ // Return if everything is within the boundary
+ if (offset <= db->length - length)
+ return 1;
- // Read object
- ssize_t bytes_read = pread(fd, buffer, length, objects->offset + offset);
+ DEBUG(db->ctx, "Database read check failed at %p for %zu byte(s)\n", p, length);
+ 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
- if (bytes_read < (ssize_t)length) {
- ERROR(db->ctx, "Error reading object from database: %m\n");
+ // Otherwise raise EFAULT
+ errno = EFAULT;
+ 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;
}
- // Success!
- return buffer;
+ // Calculate offset
+ 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) {
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) {
const struct loc_database_header_v1* header =
(const struct loc_database_header_v1*)(db->data + LOC_DATABASE_MAGIC_SIZE);
if (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
- r = loc_stringpool_open(db->ctx, &db->pool, db->f,
- be32toh(header->pool_length), be32toh(header->pool_offset));
+ r = loc_stringpool_open(db->ctx, &db->pool, stringpool_start, stringpool_length);
if (r)
return r;
// Returns the AS at position 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* p_v1;
+ struct loc_database_as_v1* as_v1 = NULL;
int r;
if ((size_t)pos >= db->as_objects.count) {
switch (db->version) {
case LOC_DATABASE_VERSION_1:
- // Read the object
- p_v1 = loc_database_read_object(db, &as_v1, &db->as_objects, pos);
- if (!p_v1)
+ // Find the object
+ as_v1 = (struct loc_database_as_v1*)loc_database_object(db,
+ &db->as_objects, sizeof(*as_v1), pos);
+ if (!as_v1)
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;
default:
// Returns the network at position pos
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 loc_database_network_v1 network_v1;
- struct loc_database_network_v1* p_v1;
+ struct loc_database_network_v1* network_v1 = NULL;
int r;
if ((size_t)pos >= db->network_objects.count) {
switch (db->version) {
case LOC_DATABASE_VERSION_1:
// Read the object
- p_v1 = loc_database_read_object(db, &network_v1, &db->network_objects, pos);
- if (!p_v1)
+ network_v1 = (struct loc_database_network_v1*)loc_database_object(db,
+ &db->network_objects, sizeof(*network_v1), pos);
+ if (!network_v1)
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;
default:
static int __loc_database_lookup(struct loc_database* db, const struct in6_addr* address,
struct loc_network** network, struct in6_addr* network_address,
off_t node_index, unsigned int level) {
- struct loc_database_network_node_v1 node_v1;
- struct loc_database_network_node_v1* p_v1;
+ struct loc_database_network_node_v1* node_v1 = NULL;
int r;
// Fetch the next node
- p_v1 = loc_database_read_object(db, &node_v1, &db->network_node_objects, node_index);
- if (!p_v1)
+ node_v1 = (struct loc_database_network_node_v1*)loc_database_object(db,
+ &db->network_node_objects, sizeof(*node_v1), node_index);
+ if (!node_v1)
return 1;
// Follow the path
loc_address_set_bit(network_address, level, bit);
if (bit == 0)
- node_index = be32toh(p_v1->zero);
+ node_index = be32toh(node_v1->zero);
else
- node_index = be32toh(p_v1->one);
+ node_index = be32toh(node_v1->one);
// If the node index is zero, the tree ends here
// and we cannot descend any further
}
// If this node has a leaf, we will check if it matches
- if (__loc_database_node_is_leaf(p_v1)) {
- r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, p_v1);
+ if (__loc_database_node_is_leaf(node_v1)) {
+ r = __loc_database_lookup_handle_leaf(db, address, network, network_address, level, node_v1);
if (r <= 0)
return r;
}
// Returns the country at position pos
static int loc_database_fetch_country(struct loc_database* db,
struct loc_country** country, off_t pos) {
- struct loc_database_country_v1 country_v1;
- struct loc_database_country_v1* p_v1;
+ struct loc_database_country_v1* country_v1 = NULL;
int r;
// Check if the country is within range
switch (db->version) {
case LOC_DATABASE_VERSION_1:
// Read the object
- p_v1 = loc_database_read_object(db, &country_v1, &db->country_objects, pos);
- if (!p_v1)
+ country_v1 = (struct loc_database_country_v1*)loc_database_object(db,
+ &db->country_objects, sizeof(*country_v1), pos);
+ if (!country_v1)
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;
default:
static int __loc_database_enumerator_next_network(
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
while (1) {
*network = loc_network_list_pop_first(enumerator->stack);
enumerator->networks_visited[node->offset]++;
// Pop node from top of the stack
- struct loc_database_network_node_v1* n = loc_database_read_object(enumerator->db,
- &node_v1, &enumerator->db->network_node_objects, node->offset);
+ struct loc_database_network_node_v1* n =
+ (struct loc_database_network_node_v1*)loc_database_object(enumerator->db,
+ &enumerator->db->network_node_objects, sizeof(*n), node->offset);
if (!n)
return 1;
#include <libloc/private.h>
#include <libloc/stringpool.h>
+#define LOC_STRINGPOOL_BLOCK_SIZE (512 * 1024)
+
struct loc_stringpool {
struct loc_ctx* ctx;
int refcount;
- // A file descriptor when we open an existing stringpool
- int fd;
-
- off_t offset;
+ // Reference to any mapped data
+ const char* data;
ssize_t length;
- // Mapped data (from mmap())
- char* mmapped_data;
-
- char* data;
- char* pos;
-
- char buffer[LOC_DATABASE_PAGE_SIZE];
+ // Reference to own storage
+ char* blocks;
+ size_t size;
};
-static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
- if (pos < pool->data) {
- errno = EFAULT;
- return -1;
- }
-
- if (pos > (pool->data + pool->length)) {
- errno = EFAULT;
- return -1;
- }
+static int loc_stringpool_grow(struct loc_stringpool* pool, const size_t size) {
+ DEBUG(pool->ctx, "Growing string pool by %zu byte(s)\n", size);
- return pos - pool->data;
-}
-
-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;
- }
+ // Increment size
+ pool->size += size;
- // Return any data that we have in memory
- if (pool->data)
- return pool->data + offset;
-
- // 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)
+ // Reallocate blocks
+ pool->blocks = realloc(pool->blocks, pool->size);
+ if (!pool->blocks) {
+ ERROR(pool->ctx, "Could not grow string pool: %m\n");
return 1;
+ }
- pool->length = length;
-
- // Restore pos
- pool->pos = __loc_stringpool_get(pool, pos);
+ // Update data pointer
+ pool->data = pool->blocks;
return 0;
}
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
- int r = loc_stringpool_grow(pool, pool->length + strlen(string) + 1);
- if (r)
- return -1;
+ if (pool->length + length > pool->size) {
+ int r = loc_stringpool_grow(pool, LOC_STRINGPOOL_BLOCK_SIZE);
+ if (r)
+ return r;
+ }
- off_t offset = loc_stringpool_get_offset(pool, pool->pos);
+ off_t offset = pool->length;
- // Copy string byte by byte
- while (*string)
- *pool->pos++ = *string++;
+ // Copy the string
+ memcpy(pool->blocks + offset, string, length);
- // Terminate the string
- *pool->pos++ = '\0';
+ // Update the length of the pool
+ pool->length += length;
return offset;
}
static void loc_stringpool_free(struct loc_stringpool* 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
- if (pool->data)
- free(pool->data);
+ if (pool->blocks)
+ free(pool->blocks);
loc_unref(pool->ctx);
free(pool);
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,
- FILE* f, size_t length, off_t offset) {
+ const char* data, const size_t length) {
struct loc_stringpool* p = NULL;
// Allocate a new stringpool
if (r)
goto ERROR;
- // Store offset and length
- p->offset = offset;
+ // Store data and length
+ p->data = data;
p->length = length;
- DEBUG(p->ctx, "Reading string pool starting from %jd (%zu bytes)\n",
- (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;
- }
+ DEBUG(p->ctx, "Opened string pool at %p (%zu bytes)\n", p->data, p->length);
*pool = p;
return 0;
}
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) {
- return loc_stringpool_get_offset(pool, pool->pos);
+ return pool->length;
}
static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {