]> git.ipfire.org Git - people/ms/libloc.git/blame - src/writer.c
Add license attribute to the database
[people/ms/libloc.git] / src / writer.c
CommitLineData
c182393f
MT
1/*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2017 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15*/
16
c182393f
MT
17#include <endian.h>
18#include <errno.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
438db08c 22#include <sys/queue.h>
c182393f
MT
23#include <time.h>
24
9fc7f001
MT
25#include <loc/libloc.h>
26#include <loc/as.h>
c182393f 27#include <loc/format.h>
3b5f4af2 28#include <loc/network.h>
9fc7f001 29#include <loc/private.h>
c182393f
MT
30#include <loc/writer.h>
31
c182393f
MT
32struct loc_writer {
33 struct loc_ctx* ctx;
34 int refcount;
35
36 struct loc_stringpool* pool;
37 off_t vendor;
38 off_t description;
4bf49d00 39 off_t license;
c182393f
MT
40
41 struct loc_as** as;
42 size_t as_count;
3b5f4af2
MT
43
44 struct loc_network_tree* networks;
c182393f
MT
45};
46
47LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer) {
48 struct loc_writer* w = calloc(1, sizeof(*w));
49 if (!w)
50 return -ENOMEM;
51
52 w->ctx = loc_ref(ctx);
53 w->refcount = 1;
54
0e974d4b 55 int r = loc_stringpool_new(ctx, &w->pool);
c182393f
MT
56 if (r) {
57 loc_writer_unref(w);
58 return r;
59 }
60
3b5f4af2
MT
61 // Initialize the network tree
62 r = loc_network_tree_new(ctx, &w->networks);
63 if (r) {
64 loc_writer_unref(w);
65 return r;
66 }
67
c182393f
MT
68 *writer = w;
69 return 0;
70}
71
72LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
73 writer->refcount++;
74
75 return writer;
76}
77
78static void loc_writer_free(struct loc_writer* writer) {
79 DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
80
81 // Unref all AS
82 for (unsigned int i = 0; i < writer->as_count; i++) {
83 loc_as_unref(writer->as[i]);
84 }
85
3b5f4af2
MT
86 // Release network tree
87 if (writer->networks)
88 loc_network_tree_unref(writer->networks);
89
c182393f
MT
90 // Unref the string pool
91 loc_stringpool_unref(writer->pool);
92
93 loc_unref(writer->ctx);
94 free(writer);
95}
96
97LOC_EXPORT struct loc_writer* loc_writer_unref(struct loc_writer* writer) {
98 if (--writer->refcount > 0)
99 return writer;
100
101 loc_writer_free(writer);
102
103 return NULL;
104}
105
106LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
107 return loc_stringpool_get(writer->pool, writer->vendor);
108}
109
110LOC_EXPORT int loc_writer_set_vendor(struct loc_writer* writer, const char* vendor) {
111 // Add the string to the string pool
112 off_t offset = loc_stringpool_add(writer->pool, vendor);
113 if (offset < 0)
114 return offset;
115
116 writer->vendor = offset;
117 return 0;
118}
119
120LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
121 return loc_stringpool_get(writer->pool, writer->description);
122}
123
124LOC_EXPORT int loc_writer_set_description(struct loc_writer* writer, const char* description) {
125 // Add the string to the string pool
126 off_t offset = loc_stringpool_add(writer->pool, description);
127 if (offset < 0)
128 return offset;
129
130 writer->description = offset;
131 return 0;
132}
133
4bf49d00
MT
134LOC_EXPORT const char* loc_writer_get_license(struct loc_writer* writer) {
135 return loc_stringpool_get(writer->pool, writer->license);
136}
137
138LOC_EXPORT int loc_writer_set_license(struct loc_writer* writer, const char* license) {
139 // Add the string to the string pool
140 off_t offset = loc_stringpool_add(writer->pool, license);
141 if (offset < 0)
142 return offset;
143
144 writer->license = offset;
145 return 0;
146}
147
c182393f
MT
148static int __loc_as_cmp(const void* as1, const void* as2) {
149 return loc_as_cmp(*(struct loc_as**)as1, *(struct loc_as**)as2);
150}
151
152LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
29bd8211 153 int r = loc_as_new(writer->ctx, as, number);
c182393f
MT
154 if (r)
155 return r;
156
157 // We have a new AS to add
158 writer->as_count++;
159
160 // Make space
161 writer->as = realloc(writer->as, sizeof(*writer->as) * writer->as_count);
162 if (!writer->as)
163 return -ENOMEM;
164
165 // Add as last element
166 writer->as[writer->as_count - 1] = loc_as_ref(*as);
167
168 // Sort everything
169 qsort(writer->as, writer->as_count, sizeof(*writer->as), __loc_as_cmp);
170
171 return 0;
172}
173
3b5f4af2
MT
174LOC_EXPORT int loc_writer_add_network(struct loc_writer* writer, struct loc_network** network, const char* string) {
175 int r;
176
177 // Create a new network object
178 r = loc_network_new_from_string(writer->ctx, network, string);
179 if (r)
180 return r;
181
182 // Add it to the local tree
183 return loc_network_tree_add_network(writer->networks, *network);
184}
185
c182393f
MT
186static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic) {
187 // Copy magic bytes
188 for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
189 magic->magic[i] = LOC_DATABASE_MAGIC[i];
190
191 // Set version
0676cd80 192 magic->version = htobe16(LOC_DATABASE_VERSION);
c182393f
MT
193}
194
195static void align_page_boundary(off_t* offset, FILE* f) {
196 // Move to next page boundary
197 while (*offset % LOC_DATABASE_PAGE_SIZE > 0)
198 *offset += fwrite("", 1, 1, f);
199}
200
201static int loc_database_write_pool(struct loc_writer* writer,
202 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
203 // Save the offset of the pool section
204 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", *offset);
0676cd80 205 header->pool_offset = htobe32(*offset);
c182393f
MT
206
207 // Write the pool
208 size_t pool_length = loc_stringpool_write(writer->pool, f);
209 *offset += pool_length;
210
211 DEBUG(writer->ctx, "Pool has a length of %zu bytes\n", pool_length);
0676cd80 212 header->pool_length = htobe32(pool_length);
c182393f
MT
213
214 return 0;
215}
216
217static int loc_database_write_as_section(struct loc_writer* writer,
218 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
219 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", *offset);
0676cd80 220 header->as_offset = htobe32(*offset);
c182393f
MT
221
222 size_t as_length = 0;
223
224 struct loc_database_as_v0 as;
225 for (unsigned int i = 0; i < writer->as_count; i++) {
226 // Convert AS into database format
29bd8211 227 loc_as_to_database_v0(writer->as[i], writer->pool, &as);
c182393f
MT
228
229 // Write to disk
65862405 230 *offset += fwrite(&as, 1, sizeof(as), f);
c182393f
MT
231 as_length += sizeof(as);
232 }
233
234 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", as_length);
0676cd80 235 header->as_length = htobe32(as_length);
c182393f 236
fda89f24
MT
237 align_page_boundary(offset, f);
238
c182393f
MT
239 return 0;
240}
241
438db08c
MT
242struct node {
243 TAILQ_ENTRY(node) nodes;
f3e02bc5 244
438db08c 245 struct loc_network_tree_node* node;
f3e02bc5 246
438db08c
MT
247 // Indices of the child nodes
248 uint32_t index_zero;
249 uint32_t index_one;
250};
f3e02bc5 251
438db08c
MT
252static struct node* make_node(struct loc_network_tree_node* node) {
253 struct node* n = malloc(sizeof(*n));
254 if (!n)
255 return NULL;
f3e02bc5 256
438db08c
MT
257 n->node = loc_network_tree_node_ref(node);
258 n->index_zero = n->index_one = 0;
259
260 return n;
261}
262
263static void free_node(struct node* node) {
264 loc_network_tree_node_unref(node->node);
265
266 free(node);
267}
268
269struct network {
270 TAILQ_ENTRY(network) networks;
271
272 struct loc_network* network;
273};
274
275static struct network* make_network(struct loc_network* network) {
276 struct network* n = malloc(sizeof(*n));
277 if (!n)
278 return NULL;
279
280 n->network = loc_network_ref(network);
281
282 return n;
f3e02bc5
MT
283}
284
438db08c
MT
285static void free_network(struct network* network) {
286 loc_network_unref(network->network);
287
288 free(network);
289}
290
291static int loc_database_write_networks(struct loc_writer* writer,
f3e02bc5 292 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
438db08c
MT
293 // Write the network tree
294 DEBUG(writer->ctx, "Network tree starts at %jd bytes\n", *offset);
295 header->network_tree_offset = htobe32(*offset);
f3e02bc5 296
438db08c
MT
297 size_t network_tree_length = 0;
298 size_t network_data_length = 0;
f3e02bc5 299
438db08c
MT
300 struct node* node;
301 struct node* child_node;
302
303 uint32_t index = 0;
304 uint32_t network_index = 0;
305
306 struct loc_database_network_v0 db_network;
307 struct loc_database_network_node_v0 db_node;
308
309 // Initialize queue for nodes
310 TAILQ_HEAD(node_t, node) nodes;
311 TAILQ_INIT(&nodes);
312
313 // Initialize queue for networks
314 TAILQ_HEAD(network_t, network) networks;
315 TAILQ_INIT(&networks);
316
317 // Add root
318 struct loc_network_tree_node* root = loc_network_tree_get_root(writer->networks);
319 node = make_node(root);
320
321 TAILQ_INSERT_TAIL(&nodes, node, nodes);
322
323 while (!TAILQ_EMPTY(&nodes)) {
324 // Pop first node in list
325 node = TAILQ_FIRST(&nodes);
326 TAILQ_REMOVE(&nodes, node, nodes);
327
328 DEBUG(writer->ctx, "Processing node %p\n", node);
329
330 // Get child nodes
331 struct loc_network_tree_node* node_zero = loc_network_tree_node_get(node->node, 0);
332 if (node_zero) {
333 node->index_zero = ++index;
334
335 child_node = make_node(node_zero);
336 loc_network_tree_node_unref(node_zero);
337
338 TAILQ_INSERT_TAIL(&nodes, child_node, nodes);
339 }
340
341 struct loc_network_tree_node* node_one = loc_network_tree_node_get(node->node, 1);
342 if (node_one) {
343 node->index_one = ++index;
344
345 child_node = make_node(node_one);
346 loc_network_tree_node_unref(node_one);
347
348 TAILQ_INSERT_TAIL(&nodes, child_node, nodes);
349 }
350
351 // Prepare what we are writing to disk
352 if (loc_network_tree_node_is_leaf(node->node)) {
353 struct loc_network* network = loc_network_tree_node_get_network(node->node);
354
355 // Append network to be written out later
356 struct network* nw = make_network(network);
357 TAILQ_INSERT_TAIL(&networks, nw, networks);
358
359 db_node.zero = htobe32(0xffffffff);
360 db_node.one = htobe32(network_index++);
361
362 loc_network_unref(network);
363 } else {
364 db_node.zero = htobe32(node->index_zero);
365 db_node.one = htobe32(node->index_one);
366 }
367
368 // Write the current node
369 DEBUG(writer->ctx, "Writing node %p (0 = %d, 1 = %d)\n",
370 node, node->index_zero, node->index_one);
371
372 *offset += fwrite(&db_node, 1, sizeof(db_node), f);
373 network_tree_length += sizeof(db_node);
374
375 free_node(node);
376 }
377
378 loc_network_tree_node_unref(root);
379
380 header->network_tree_length = htobe32(network_tree_length);
381
382 align_page_boundary(offset, f);
383
384 DEBUG(writer->ctx, "Networks data section starts at %jd bytes\n", *offset);
385 header->network_data_offset = htobe32(*offset);
386
387 // We have now written the entire tree and have all networks
388 // in a queue in order as they are indexed
389 while (!TAILQ_EMPTY(&networks)) {
390 struct network* nw = TAILQ_FIRST(&networks);
391 TAILQ_REMOVE(&networks, nw, networks);
392
393 // Prepare what we are writing to disk
394 int r = loc_network_to_database_v0(nw->network, &db_network);
395 if (r)
396 return r;
397
398 *offset += fwrite(&db_network, 1, sizeof(db_network), f);
399 network_data_length += sizeof(db_network);
400
401 free_network(nw);
402 }
f3e02bc5 403
438db08c 404 header->network_data_length = htobe32(network_data_length);
f3e02bc5 405
fda89f24
MT
406 align_page_boundary(offset, f);
407
f3e02bc5
MT
408 return 0;
409}
410
c182393f
MT
411LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
412 struct loc_database_magic magic;
413 make_magic(writer, &magic);
414
415 // Make the header
416 struct loc_database_header_v0 header;
0676cd80
MT
417 header.vendor = htobe32(writer->vendor);
418 header.description = htobe32(writer->description);
4bf49d00 419 header.license = htobe32(writer->license);
c182393f
MT
420
421 time_t now = time(NULL);
422 header.created_at = htobe64(now);
423
424 int r;
425 off_t offset = 0;
426
427 // Start writing at the beginning of the file
428 r = fseek(f, 0, SEEK_SET);
429 if (r)
430 return r;
431
432 // Write the magic
433 offset += fwrite(&magic, 1, sizeof(magic), f);
434
435 // Skip the space we need to write the header later
436 r = fseek(f, sizeof(header), SEEK_CUR);
437 if (r) {
438 DEBUG(writer->ctx, "Could not seek to position after header\n");
439 return r;
440 }
441 offset += sizeof(header);
442
443 align_page_boundary(&offset, f);
444
445 // Write all ASes
446 r = loc_database_write_as_section(writer, &header, &offset, f);
447 if (r)
448 return r;
449
f3e02bc5 450 // Write all networks
438db08c 451 r = loc_database_write_networks(writer, &header, &offset, f);
f3e02bc5
MT
452 if (r)
453 return r;
454
c182393f
MT
455 // Write pool
456 r = loc_database_write_pool(writer, &header, &offset, f);
457 if (r)
458 return r;
459
460 // Write the header
461 r = fseek(f, sizeof(magic), SEEK_SET);
462 if (r)
463 return r;
464
465 offset += fwrite(&header, 1, sizeof(header), f);
466
467 return 0;
468}