X-Git-Url: http://git.ipfire.org/?p=people%2Fms%2Flibloc.git;a=blobdiff_plain;f=src%2Fdatabase.c;h=e5460da3bb25bc5b49f1d2af9fc8f1f3985d7cf6;hp=85e3912fc0c4d37c4bec76992f342a97204485e6;hb=8f3e2a0611d39768b250ad142756ce1b4cfd1ff0;hpb=2601e83eca9f5d8447186256c642aef25441f07e diff --git a/src/database.c b/src/database.c index 85e3912..e5460da 100644 --- a/src/database.c +++ b/src/database.c @@ -21,11 +21,16 @@ #include #include #include +#include #include +#include +#include #include +#include #include "libloc-private.h" +#include "as.h" #include "database.h" #include "stringpool.h" @@ -33,122 +38,24 @@ struct loc_database { struct loc_ctx* ctx; int refcount; + FILE* file; unsigned int version; + time_t created_at; off_t vendor; off_t description; - struct loc_stringpool* pool; -}; - -const char* LOC_DATABASE_MAGIC = "LOCDBXX"; -unsigned int LOC_DATABASE_VERSION = 0; - -struct loc_database_magic { - char magic[7]; - - // Database version information - uint8_t version; -}; - -struct loc_database_header_v0 { - // Vendor who created the database - uint32_t vendor; - - // Description of the database - uint32_t description; + // ASes in the database + struct loc_database_as_v0* as_v0; + size_t as_count; - // Tells us where the pool starts - uint32_t pool_offset; - uint32_t pool_length; + struct loc_stringpool* pool; }; -LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, size_t pool_size) { - struct loc_database* db = calloc(1, sizeof(*db)); - if (!db) - return -ENOMEM; - - // Reference context - db->ctx = loc_ref(ctx); - db->refcount = 1; - - DEBUG(db->ctx, "Database allocated at %p\n", db); - - // Create string pool - int r = loc_stringpool_new(db->ctx, &db->pool, pool_size); - if (r) { - loc_database_unref(db); - return r; - } - - *database = db; - - return 0; -} - -LOC_EXPORT int loc_database_open(struct loc_ctx* ctx, struct loc_database** database, FILE* f) { - int r = loc_database_new(ctx, database, 0); - if (r) - return r; - - return loc_database_read(*database, f); -} - -LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) { - db->refcount++; - - return db; -} - -static void loc_database_free(struct loc_database* db) { - DEBUG(db->ctx, "Releasing database %p\n", db); - - loc_stringpool_unref(db->pool); - - loc_unref(db->ctx); - free(db); -} - -LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) { - if (--db->refcount > 0) - return NULL; - - loc_database_free(db); - return NULL; -} - -LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) { - return loc_stringpool_get(db->pool, db->vendor); -} - -LOC_EXPORT int loc_database_set_vendor(struct loc_database* db, const char* vendor) { - // Add the string to the string pool - off_t offset = loc_stringpool_add(db->pool, vendor); - if (offset < 0) - return offset; - - db->vendor = offset; - return 0; -} - -LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) { - return loc_stringpool_get(db->pool, db->description); -} - -LOC_EXPORT int loc_database_set_description(struct loc_database* db, const char* description) { - // Add the string to the string pool - off_t offset = loc_stringpool_add(db->pool, description); - if (offset < 0) - return offset; - - db->description = offset; - return 0; -} - -static int loc_database_read_magic(struct loc_database* db, FILE* f) { +static int loc_database_read_magic(struct loc_database* db) { struct loc_database_magic magic; // Read from file - size_t bytes_read = fread(&magic, 1, sizeof(magic), f); + size_t bytes_read = fread(&magic, 1, sizeof(magic), db->file); // Check if we have been able to read enough data if (bytes_read < sizeof(magic)) { @@ -174,11 +81,30 @@ static int loc_database_read_magic(struct loc_database* db, FILE* f) { return 1; } -static int loc_database_read_header_v0(struct loc_database* db, FILE* f) { +static int loc_database_read_as_section_v0(struct loc_database* db, + off_t as_offset, size_t as_length) { + DEBUG(db->ctx, "Reading AS section from %jd (%zu bytes)\n", as_offset, as_length); + + if (as_length > 0) { + db->as_v0 = mmap(NULL, as_length, PROT_READ, + MAP_SHARED, fileno(db->file), as_offset); + + if (db->as_v0 == MAP_FAILED) + return -errno; + } + + db->as_count = as_length / sizeof(*db->as_v0); + + INFO(db->ctx, "Read %zu ASes from the database\n", db->as_count); + + return 0; +} + +static int loc_database_read_header_v0(struct loc_database* db) { struct loc_database_header_v0 header; // Read from file - size_t size = fread(&header, 1, sizeof(header), f); + size_t size = fread(&header, 1, sizeof(header), db->file); if (size < sizeof(header)) { ERROR(db->ctx, "Could not read enough data for header\n"); @@ -186,6 +112,7 @@ static int loc_database_read_header_v0(struct loc_database* db, FILE* f) { } // Copy over data + db->created_at = be64toh(header.created_at); db->vendor = ntohl(header.vendor); db->description = ntohl(header.description); @@ -193,17 +120,26 @@ static int loc_database_read_header_v0(struct loc_database* db, FILE* f) { off_t pool_offset = ntohl(header.pool_offset); size_t pool_length = ntohl(header.pool_length); - int r = loc_stringpool_read(db->pool, f, pool_offset, pool_length); + int r = loc_stringpool_open(db->ctx, &db->pool, + db->file, pool_length, pool_offset); + if (r) + return r; + + // AS section + off_t as_offset = ntohl(header.as_offset); + size_t as_length = ntohl(header.as_length); + + r = loc_database_read_as_section_v0(db, as_offset, as_length); if (r) return r; return 0; } -static int loc_database_read_header(struct loc_database* db, FILE* f) { +static int loc_database_read_header(struct loc_database* db) { switch (db->version) { case 0: - return loc_database_read_header_v0(db, f); + return loc_database_read_header_v0(db); default: ERROR(db->ctx, "Incompatible database version: %u\n", db->version); @@ -211,76 +147,166 @@ static int loc_database_read_header(struct loc_database* db, FILE* f) { } } -LOC_EXPORT int loc_database_read(struct loc_database* db, FILE* f) { - int r = fseek(f, 0, SEEK_SET); - if (r) - return r; +static FILE* copy_file_pointer(FILE* f) { + int fd = fileno(f); + + // Make a copy + fd = dup(fd); + + return fdopen(fd, "r"); +} + +LOC_EXPORT int loc_database_new(struct loc_ctx* ctx, struct loc_database** database, FILE* f) { + struct loc_database* db = calloc(1, sizeof(*db)); + if (!db) + return -ENOMEM; + + // Reference context + db->ctx = loc_ref(ctx); + db->refcount = 1; + + DEBUG(db->ctx, "Database object allocated at %p\n", db); + + // Copy the file pointer and work on that so we don't care if + // the calling function closes the file + db->file = copy_file_pointer(f); + if (!db->file) + goto FAIL; // Read magic bytes - r = loc_database_read_magic(db, f); + int r = loc_database_read_magic(db); if (r) return r; // Read the header - r = loc_database_read_header(db, f); + r = loc_database_read_header(db); if (r) return r; + *database = db; + return 0; + +FAIL: + loc_database_unref(db); + + return -errno; } -static void loc_database_make_magic(struct loc_database* db, struct loc_database_magic* magic) { - // Copy magic bytes - for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++) - magic->magic[i] = LOC_DATABASE_MAGIC[i]; +LOC_EXPORT struct loc_database* loc_database_ref(struct loc_database* db) { + db->refcount++; - // Set version - magic->version = htons(LOC_DATABASE_VERSION); + return db; } -LOC_EXPORT int loc_database_write(struct loc_database* db, FILE* f) { - struct loc_database_magic magic; - loc_database_make_magic(db, &magic); +static void loc_database_free(struct loc_database* db) { + DEBUG(db->ctx, "Releasing database %p\n", db); - // Make the header - struct loc_database_header_v0 header; - header.vendor = htonl(db->vendor); - header.description = htonl(db->description); + // Removing all ASes + if (db->as_v0) { + int r = munmap(db->as_v0, db->as_count * sizeof(*db->as_v0)); + if (r) + ERROR(db->ctx, "Could not unmap AS section: %s\n", strerror(errno)); + } - int r; - off_t offset = 0; + loc_stringpool_unref(db->pool); - // Start writing at the beginning of the file - r = fseek(f, 0, SEEK_SET); - if (r) - return r; + // Close file + if (db->file) + fclose(db->file); - // Write the magic - offset += fwrite(&magic, 1, sizeof(magic), f); + loc_unref(db->ctx); + free(db); +} - // Skip the space we need to write the header later - r = fseek(f, sizeof(header), SEEK_CUR); - if (r) { - DEBUG(db->ctx, "Could not seek to position after header\n"); - return r; +LOC_EXPORT struct loc_database* loc_database_unref(struct loc_database* db) { + if (--db->refcount > 0) + return NULL; + + loc_database_free(db); + return NULL; +} + +LOC_EXPORT time_t loc_database_created_at(struct loc_database* db) { + return db->created_at; +} + +LOC_EXPORT const char* loc_database_get_vendor(struct loc_database* db) { + return loc_stringpool_get(db->pool, db->vendor); +} + +LOC_EXPORT const char* loc_database_get_description(struct loc_database* db) { + return loc_stringpool_get(db->pool, db->description); +} + +LOC_EXPORT size_t loc_database_count_as(struct loc_database* db) { + return db->as_count; +} + +// Returns the AS at position pos +static int loc_database_fetch_as(struct loc_database* db, struct loc_as** as, off_t pos) { + if ((size_t)pos >= db->as_count) + return -EINVAL; + + DEBUG(db->ctx, "Fetching AS at position %jd\n", pos); + + int r; + switch (db->version) { + case 0: + r = loc_as_new_from_database_v0(db->ctx, db->pool, as, db->as_v0 + pos); + break; + + default: + return -1; } - offset += sizeof(header); - // Save the offset of the pool section - DEBUG(db->ctx, "Pool starts at %jd bytes\n", offset); - header.pool_offset = htonl(offset); + if (r == 0) { + DEBUG(db->ctx, "Got AS%u\n", loc_as_get_number(*as)); + } - // Size of the pool - size_t pool_length = loc_stringpool_write(db->pool, f); - DEBUG(db->ctx, "Pool has a length of %zu bytes\n", pool_length); - header.pool_length = htonl(pool_length); + return r; +} - // Write the header - r = fseek(f, sizeof(magic), SEEK_SET); - if (r) - return r; +// Performs a binary search to find the AS in the list +LOC_EXPORT int loc_database_get_as(struct loc_database* db, struct loc_as** as, uint32_t number) { + off_t lo = 0; + off_t hi = db->as_count - 1; - offset += fwrite(&header, 1, sizeof(header), f); + // Save start time + clock_t start = clock(); - return 0; + while (lo <= hi) { + off_t i = (lo + hi) / 2; + + // Fetch AS in the middle between lo and hi + int r = loc_database_fetch_as(db, as, i); + if (r) + return r; + + // Check if this is a match + uint32_t as_number = loc_as_get_number(*as); + if (as_number == number) { + clock_t end = clock(); + + // Log how fast this has been + DEBUG(db->ctx, "Found AS%u in %.8fs\n", as_number, + (double)(end - start) / CLOCKS_PER_SEC); + + return 0; + } + + // If it wasn't, we release the AS and + // adjust our search pointers + loc_as_unref(*as); + + if (as_number < number) { + lo = i + 1; + } else + hi = i - 1; + } + + // Nothing found + *as = NULL; + + return 1; }