]> git.ipfire.org Git - people/ms/libloc.git/blame - src/stringpool.c
stringpool: Do not call strlen() on potential NULL pointer
[people/ms/libloc.git] / src / stringpool.c
CommitLineData
62b83e6d
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
17#include <errno.h>
18#include <stddef.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
6809d5ac
MT
22#include <sys/mman.h>
23#include <unistd.h>
62b83e6d
MT
24
25#include <loc/libloc.h>
c34e76f1 26#include <loc/format.h>
9fc7f001
MT
27#include <loc/private.h>
28#include <loc/stringpool.h>
62b83e6d 29
0e974d4b
MT
30enum loc_stringpool_mode {
31 STRINGPOOL_DEFAULT,
32 STRINGPOOL_MMAP,
33};
34
62b83e6d
MT
35struct loc_stringpool {
36 struct loc_ctx* ctx;
62b83e6d 37 int refcount;
0e974d4b
MT
38
39 enum loc_stringpool_mode mode;
40
62b83e6d 41 char* data;
0e974d4b 42 ssize_t length;
62b83e6d 43
0e974d4b 44 char* pos;
62b83e6d
MT
45};
46
010fe9b6
MT
47static off_t loc_stringpool_get_offset(struct loc_stringpool* pool, const char* pos) {
48 if (pos < pool->data)
49 return -EFAULT;
50
51 if (pos > (pool->data + pool->length))
52 return -EFAULT;
53
54 return pos - pool->data;
55}
56
57static char* __loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
58 if (offset < 0 || offset >= pool->length)
59 return NULL;
60
61 return pool->data + offset;
62}
63
64static int loc_stringpool_grow(struct loc_stringpool* pool, size_t length) {
65 DEBUG(pool->ctx, "Growing string pool to %zu bytes\n", length);
66
67 // Save pos pointer
68 off_t pos = loc_stringpool_get_offset(pool, pool->pos);
69
70 // Reallocate data section
71 pool->data = realloc(pool->data, length);
72 if (!pool->data)
73 return -ENOMEM;
74
75 pool->length = length;
76
77 // Restore pos
78 pool->pos = __loc_stringpool_get(pool, pos);
79
80 return 0;
81}
82
83static off_t loc_stringpool_append(struct loc_stringpool* pool, const char* string) {
bf4ac66f 84 if (!string)
010fe9b6
MT
85 return -EINVAL;
86
87 DEBUG(pool->ctx, "Appending '%s' to string pool at %p\n", string, pool);
88
89 // Make sure we have enough space
90 int r = loc_stringpool_grow(pool, pool->length + strlen(string) + 1);
91 if (r) {
92 errno = r;
93 return -1;
94 }
95
96 off_t offset = loc_stringpool_get_offset(pool, pool->pos);
97
98 // Copy string byte by byte
99 while (*string)
100 *pool->pos++ = *string++;
101
102 // Terminate the string
103 *pool->pos++ = '\0';
104
105 return offset;
106}
107
0e974d4b
MT
108static int __loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool, enum loc_stringpool_mode mode) {
109 struct loc_stringpool* p = calloc(1, sizeof(*p));
110 if (!p)
111 return -ENOMEM;
112
113 p->ctx = loc_ref(ctx);
114 p->refcount = 1;
6809d5ac 115
0e974d4b
MT
116 // Save mode
117 p->mode = mode;
118
119 *pool = p;
6809d5ac
MT
120
121 return 0;
122}
123
0e974d4b 124LOC_EXPORT int loc_stringpool_new(struct loc_ctx* ctx, struct loc_stringpool** pool) {
010fe9b6
MT
125 int r = __loc_stringpool_new(ctx, pool, STRINGPOOL_DEFAULT);
126 if (r)
127 return r;
128
129 // Add an empty string to new string pools
130 loc_stringpool_append(*pool, "");
131
132 return r;
0e974d4b 133}
2601e83e 134
0e974d4b
MT
135static int loc_stringpool_mmap(struct loc_stringpool* pool, FILE* f, size_t length, off_t offset) {
136 if (pool->mode != STRINGPOOL_MMAP)
137 return -EINVAL;
6809d5ac 138
5c57de03 139 DEBUG(pool->ctx, "Reading string pool starting from %jd (%zu bytes)\n", (intmax_t)offset, length);
6809d5ac 140
0e974d4b
MT
141 // Map file content into memory
142 pool->data = pool->pos = mmap(NULL, length, PROT_READ,
143 MAP_PRIVATE, fileno(f), offset);
2601e83e 144
0e974d4b
MT
145 // Store size of section
146 pool->length = length;
6809d5ac 147
0e974d4b 148 if (pool->data == MAP_FAILED)
6809d5ac 149 return -errno;
2601e83e
MT
150
151 return 0;
152}
153
0e974d4b
MT
154LOC_EXPORT int loc_stringpool_open(struct loc_ctx* ctx, struct loc_stringpool** pool,
155 FILE* f, size_t length, off_t offset) {
156 int r = __loc_stringpool_new(ctx, pool, STRINGPOOL_MMAP);
157 if (r)
158 return r;
62b83e6d 159
0e974d4b 160 // Map data into memory
2a30e4de
MT
161 if (length > 0) {
162 r = loc_stringpool_mmap(*pool, f, length, offset);
163 if (r)
164 return r;
165 }
62b83e6d
MT
166
167 return 0;
168}
169
170LOC_EXPORT struct loc_stringpool* loc_stringpool_ref(struct loc_stringpool* pool) {
171 pool->refcount++;
172
173 return pool;
174}
175
176static void loc_stringpool_free(struct loc_stringpool* pool) {
177 DEBUG(pool->ctx, "Releasing string pool %p\n", pool);
0e974d4b
MT
178 int r;
179
180 switch (pool->mode) {
181 case STRINGPOOL_DEFAULT:
182 if (pool->data)
183 free(pool->data);
184 break;
185
186 case STRINGPOOL_MMAP:
187 if (pool->data) {
188 r = munmap(pool->data, pool->length);
189 if (r)
190 ERROR(pool->ctx, "Could not unmap data at %p: %s\n",
191 pool->data, strerror(errno));
192 }
193 break;
194 }
62b83e6d
MT
195
196 loc_unref(pool->ctx);
62b83e6d
MT
197 free(pool);
198}
199
200LOC_EXPORT struct loc_stringpool* loc_stringpool_unref(struct loc_stringpool* pool) {
201 if (--pool->refcount > 0)
202 return NULL;
203
204 loc_stringpool_free(pool);
205
206 return NULL;
207}
208
62b83e6d
MT
209static off_t loc_stringpool_get_next_offset(struct loc_stringpool* pool, off_t offset) {
210 const char* string = loc_stringpool_get(pool, offset);
1afe5545
MT
211 if (!string)
212 return offset;
62b83e6d
MT
213
214 return offset + strlen(string) + 1;
215}
216
0e974d4b
MT
217LOC_EXPORT const char* loc_stringpool_get(struct loc_stringpool* pool, off_t offset) {
218 return __loc_stringpool_get(pool, offset);
62b83e6d
MT
219}
220
2601e83e
MT
221LOC_EXPORT size_t loc_stringpool_get_size(struct loc_stringpool* pool) {
222 return loc_stringpool_get_offset(pool, pool->pos);
223}
224
62b83e6d
MT
225static off_t loc_stringpool_find(struct loc_stringpool* pool, const char* s) {
226 if (!s || !*s)
227 return -EINVAL;
228
229 off_t offset = 0;
0e974d4b 230 while (offset < pool->length) {
62b83e6d
MT
231 const char* string = loc_stringpool_get(pool, offset);
232 if (!string)
233 break;
234
235 int r = strcmp(s, string);
236 if (r == 0)
237 return offset;
238
239 offset = loc_stringpool_get_next_offset(pool, offset);
240 }
241
242 return -ENOENT;
243}
244
62b83e6d
MT
245LOC_EXPORT off_t loc_stringpool_add(struct loc_stringpool* pool, const char* string) {
246 off_t offset = loc_stringpool_find(pool, string);
247 if (offset >= 0) {
5c57de03 248 DEBUG(pool->ctx, "Found '%s' at position %jd\n", string, (intmax_t)offset);
62b83e6d
MT
249 return offset;
250 }
251
252 return loc_stringpool_append(pool, string);
253}
254
255LOC_EXPORT void loc_stringpool_dump(struct loc_stringpool* pool) {
256 off_t offset = 0;
257
0e974d4b 258 while (offset < pool->length) {
62b83e6d
MT
259 const char* string = loc_stringpool_get(pool, offset);
260 if (!string)
261 break;
262
5c57de03 263 printf("%jd (%zu): %s\n", (intmax_t)offset, strlen(string), string);
62b83e6d
MT
264
265 offset = loc_stringpool_get_next_offset(pool, offset);
266 }
267}
2601e83e 268
2601e83e
MT
269LOC_EXPORT size_t loc_stringpool_write(struct loc_stringpool* pool, FILE* f) {
270 size_t size = loc_stringpool_get_size(pool);
271
8f5b676a 272 return fwrite(pool->data, sizeof(*pool->data), size, f);
2601e83e 273}