]> git.ipfire.org Git - people/ms/libloc.git/blob - src/writer.c
1e3e360fea5b97c02c660a638a8f667e0454e74a
[people/ms/libloc.git] / src / writer.c
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
17 #include <endian.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23
24 #include <loc/format.h>
25 #include <loc/network.h>
26 #include <loc/writer.h>
27
28 #include "libloc-private.h"
29 #include "as.h"
30
31 struct loc_writer {
32 struct loc_ctx* ctx;
33 int refcount;
34
35 struct loc_stringpool* pool;
36 off_t vendor;
37 off_t description;
38
39 struct loc_as** as;
40 size_t as_count;
41
42 struct loc_network_tree* networks;
43 };
44
45 LOC_EXPORT int loc_writer_new(struct loc_ctx* ctx, struct loc_writer** writer) {
46 struct loc_writer* w = calloc(1, sizeof(*w));
47 if (!w)
48 return -ENOMEM;
49
50 w->ctx = loc_ref(ctx);
51 w->refcount = 1;
52
53 int r = loc_stringpool_new(ctx, &w->pool);
54 if (r) {
55 loc_writer_unref(w);
56 return r;
57 }
58
59 // Initialize the network tree
60 r = loc_network_tree_new(ctx, &w->networks);
61 if (r) {
62 loc_writer_unref(w);
63 return r;
64 }
65
66 *writer = w;
67 return 0;
68 }
69
70 LOC_EXPORT struct loc_writer* loc_writer_ref(struct loc_writer* writer) {
71 writer->refcount++;
72
73 return writer;
74 }
75
76 static void loc_writer_free(struct loc_writer* writer) {
77 DEBUG(writer->ctx, "Releasing writer at %p\n", writer);
78
79 // Unref all AS
80 for (unsigned int i = 0; i < writer->as_count; i++) {
81 loc_as_unref(writer->as[i]);
82 }
83
84 // Release network tree
85 if (writer->networks)
86 loc_network_tree_unref(writer->networks);
87
88 // Unref the string pool
89 loc_stringpool_unref(writer->pool);
90
91 loc_unref(writer->ctx);
92 free(writer);
93 }
94
95 LOC_EXPORT struct loc_writer* loc_writer_unref(struct loc_writer* writer) {
96 if (--writer->refcount > 0)
97 return writer;
98
99 loc_writer_free(writer);
100
101 return NULL;
102 }
103
104 LOC_EXPORT const char* loc_writer_get_vendor(struct loc_writer* writer) {
105 return loc_stringpool_get(writer->pool, writer->vendor);
106 }
107
108 LOC_EXPORT int loc_writer_set_vendor(struct loc_writer* writer, const char* vendor) {
109 // Add the string to the string pool
110 off_t offset = loc_stringpool_add(writer->pool, vendor);
111 if (offset < 0)
112 return offset;
113
114 writer->vendor = offset;
115 return 0;
116 }
117
118 LOC_EXPORT const char* loc_writer_get_description(struct loc_writer* writer) {
119 return loc_stringpool_get(writer->pool, writer->description);
120 }
121
122 LOC_EXPORT int loc_writer_set_description(struct loc_writer* writer, const char* description) {
123 // Add the string to the string pool
124 off_t offset = loc_stringpool_add(writer->pool, description);
125 if (offset < 0)
126 return offset;
127
128 writer->description = offset;
129 return 0;
130 }
131
132 static int __loc_as_cmp(const void* as1, const void* as2) {
133 return loc_as_cmp(*(struct loc_as**)as1, *(struct loc_as**)as2);
134 }
135
136 LOC_EXPORT int loc_writer_add_as(struct loc_writer* writer, struct loc_as** as, uint32_t number) {
137 int r = loc_as_new(writer->ctx, writer->pool, as, number);
138 if (r)
139 return r;
140
141 // We have a new AS to add
142 writer->as_count++;
143
144 // Make space
145 writer->as = realloc(writer->as, sizeof(*writer->as) * writer->as_count);
146 if (!writer->as)
147 return -ENOMEM;
148
149 // Add as last element
150 writer->as[writer->as_count - 1] = loc_as_ref(*as);
151
152 // Sort everything
153 qsort(writer->as, writer->as_count, sizeof(*writer->as), __loc_as_cmp);
154
155 return 0;
156 }
157
158 LOC_EXPORT int loc_writer_add_network(struct loc_writer* writer, struct loc_network** network, const char* string) {
159 int r;
160
161 // Create a new network object
162 r = loc_network_new_from_string(writer->ctx, network, string);
163 if (r)
164 return r;
165
166 // Add it to the local tree
167 return loc_network_tree_add_network(writer->networks, *network);
168 }
169
170 static void make_magic(struct loc_writer* writer, struct loc_database_magic* magic) {
171 // Copy magic bytes
172 for (unsigned int i = 0; i < strlen(LOC_DATABASE_MAGIC); i++)
173 magic->magic[i] = LOC_DATABASE_MAGIC[i];
174
175 // Set version
176 magic->version = htobe16(LOC_DATABASE_VERSION);
177 }
178
179 static void align_page_boundary(off_t* offset, FILE* f) {
180 // Move to next page boundary
181 while (*offset % LOC_DATABASE_PAGE_SIZE > 0)
182 *offset += fwrite("", 1, 1, f);
183 }
184
185 static int loc_database_write_pool(struct loc_writer* writer,
186 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
187 // Save the offset of the pool section
188 DEBUG(writer->ctx, "Pool starts at %jd bytes\n", *offset);
189 header->pool_offset = htobe32(*offset);
190
191 // Write the pool
192 size_t pool_length = loc_stringpool_write(writer->pool, f);
193 *offset += pool_length;
194
195 DEBUG(writer->ctx, "Pool has a length of %zu bytes\n", pool_length);
196 header->pool_length = htobe32(pool_length);
197
198 return 0;
199 }
200
201 static int loc_database_write_as_section(struct loc_writer* writer,
202 struct loc_database_header_v0* header, off_t* offset, FILE* f) {
203 DEBUG(writer->ctx, "AS section starts at %jd bytes\n", *offset);
204 header->as_offset = htobe32(*offset);
205
206 size_t as_length = 0;
207
208 struct loc_database_as_v0 as;
209 for (unsigned int i = 0; i < writer->as_count; i++) {
210 // Convert AS into database format
211 loc_as_to_database_v0(writer->as[i], &as);
212
213 // Write to disk
214 offset += fwrite(&as, 1, sizeof(as), f);
215 as_length += sizeof(as);
216 }
217
218 DEBUG(writer->ctx, "AS section has a length of %zu bytes\n", as_length);
219 header->as_length = htobe32(as_length);
220
221 return 0;
222 }
223
224 LOC_EXPORT int loc_writer_write(struct loc_writer* writer, FILE* f) {
225 struct loc_database_magic magic;
226 make_magic(writer, &magic);
227
228 // Make the header
229 struct loc_database_header_v0 header;
230 header.vendor = htobe32(writer->vendor);
231 header.description = htobe32(writer->description);
232
233 time_t now = time(NULL);
234 header.created_at = htobe64(now);
235
236 int r;
237 off_t offset = 0;
238
239 // Start writing at the beginning of the file
240 r = fseek(f, 0, SEEK_SET);
241 if (r)
242 return r;
243
244 // Write the magic
245 offset += fwrite(&magic, 1, sizeof(magic), f);
246
247 // Skip the space we need to write the header later
248 r = fseek(f, sizeof(header), SEEK_CUR);
249 if (r) {
250 DEBUG(writer->ctx, "Could not seek to position after header\n");
251 return r;
252 }
253 offset += sizeof(header);
254
255 align_page_boundary(&offset, f);
256
257 // Write all ASes
258 r = loc_database_write_as_section(writer, &header, &offset, f);
259 if (r)
260 return r;
261
262 align_page_boundary(&offset, f);
263
264 // Write pool
265 r = loc_database_write_pool(writer, &header, &offset, f);
266 if (r)
267 return r;
268
269 // Write the header
270 r = fseek(f, sizeof(magic), SEEK_SET);
271 if (r)
272 return r;
273
274 offset += fwrite(&header, 1, sizeof(header), f);
275
276 return 0;
277 }