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