1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Nokia Corporation
5 Contact: Maemo Multimedia <multimedia@maemo.org>
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <errno.h>
26 #include <sys/types.h>
27 #include <unistd.h>
28 #include <stdio.h>
29
30 #include <pulse/xmalloc.h>
31 #include <pulsecore/core-util.h>
32 #include <pulsecore/log.h>
33 #include <pulsecore/core-error.h>
34 #include <pulsecore/hashmap.h>
35
36 #include "database.h"
37
38 typedef struct simple_data {
39 char *filename;
40 char *tmp_filename;
41 pa_hashmap *map;
42 bool read_only;
43 } simple_data;
44
45 typedef struct entry {
46 pa_datum key;
47 pa_datum data;
48 } entry;
49
pa_datum_free(pa_datum * d)50 void pa_datum_free(pa_datum *d) {
51 pa_assert(d);
52
53 pa_xfree(d->data);
54 d->data = NULL;
55 d->size = 0;
56 }
57
compare_func(const void * a,const void * b)58 static int compare_func(const void *a, const void *b) {
59 const pa_datum *aa, *bb;
60
61 aa = (const pa_datum*)a;
62 bb = (const pa_datum*)b;
63
64 if (aa->size != bb->size)
65 return aa->size > bb->size ? 1 : -1;
66
67 return memcmp(aa->data, bb->data, aa->size);
68 }
69
70 /* pa_idxset_string_hash_func modified for our use */
hash_func(const void * p)71 static unsigned hash_func(const void *p) {
72 const pa_datum *d;
73 unsigned hash = 0;
74 const char *c;
75 unsigned i;
76
77 d = (const pa_datum*)p;
78 c = d->data;
79
80 for (i = 0; i < d->size; i++) {
81 hash = 31 * hash + (unsigned) *c;
82 c++;
83 }
84
85 return hash;
86 }
87
new_entry(const pa_datum * key,const pa_datum * data)88 static entry* new_entry(const pa_datum *key, const pa_datum *data) {
89 entry *e;
90
91 pa_assert(key);
92 pa_assert(data);
93
94 e = pa_xnew0(entry, 1);
95 e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
96 e->key.size = key->size;
97 e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
98 e->data.size = data->size;
99 return e;
100 }
101
free_entry(entry * e)102 static void free_entry(entry *e) {
103 if (e) {
104 if (e->key.data)
105 pa_xfree(e->key.data);
106 if (e->data.data)
107 pa_xfree(e->data.data);
108 pa_xfree(e);
109 }
110 }
111
read_uint(FILE * f,uint32_t * res)112 static int read_uint(FILE *f, uint32_t *res) {
113 size_t items = 0;
114 uint8_t values[4];
115 uint32_t tmp;
116 int i;
117
118 items = fread(&values, sizeof(values), sizeof(uint8_t), f);
119
120 if (feof(f)) /* EOF */
121 return 0;
122
123 if (ferror(f))
124 return -1;
125
126 for (i = 0; i < 4; ++i) {
127 tmp = values[i];
128 *res += (tmp << (i*8));
129 }
130
131 return items;
132 }
133
read_data(FILE * f,void ** data,ssize_t * length)134 static int read_data(FILE *f, void **data, ssize_t *length) {
135 size_t items = 0;
136 uint32_t data_len = 0;
137
138 pa_assert(f);
139
140 *data = NULL;
141 *length = 0;
142
143 if ((items = read_uint(f, &data_len)) <= 0)
144 return -1;
145
146 if (data_len > 0) {
147 *data = pa_xmalloc0(data_len);
148 items = fread(*data, data_len, 1, f);
149
150 if (feof(f)) /* EOF */
151 goto reset;
152
153 if (ferror(f))
154 goto reset;
155
156 *length = data_len;
157
158 } else { /* no data? */
159 return -1;
160 }
161
162 return 0;
163
164 reset:
165 pa_xfree(*data);
166 *data = NULL;
167 *length = 0;
168 return -1;
169 }
170
fill_data(simple_data * db,FILE * f)171 static int fill_data(simple_data *db, FILE *f) {
172 pa_datum key;
173 pa_datum data;
174 void *d = NULL;
175 ssize_t l = 0;
176 bool append = false;
177 enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
178
179 pa_assert(db);
180 pa_assert(db->map);
181
182 errno = 0;
183
184 key.size = 0;
185 key.data = NULL;
186
187 while (!read_data(f, &d, &l)) {
188
189 switch (field) {
190 case FIELD_KEY:
191 key.data = d;
192 key.size = l;
193 field = FIELD_DATA;
194 break;
195 case FIELD_DATA:
196 data.data = d;
197 data.size = l;
198 append = true;
199 break;
200 }
201
202 if (append) {
203 entry *e = pa_xnew0(entry, 1);
204 e->key.data = key.data;
205 e->key.size = key.size;
206 e->data.data = data.data;
207 e->data.size = data.size;
208 pa_hashmap_put(db->map, &e->key, e);
209 append = false;
210 field = FIELD_KEY;
211 }
212 }
213
214 if (ferror(f)) {
215 pa_log_warn("read error. %s", pa_cstrerror(errno));
216 pa_database_clear((pa_database*)db);
217 }
218
219 if (field == FIELD_DATA && d)
220 pa_xfree(d);
221
222 return pa_hashmap_size(db->map);
223 }
224
pa_database_get_filename_suffix(void)225 const char* pa_database_get_filename_suffix(void) {
226 return ".simple";
227 }
228
pa_database_open_internal(const char * path,bool for_write)229 pa_database* pa_database_open_internal(const char *path, bool for_write) {
230 FILE *f;
231 simple_data *db;
232
233 pa_assert(path);
234
235 errno = 0;
236
237 f = pa_fopen_cloexec(path, "r");
238
239 if (f || errno == ENOENT) { /* file not found is ok */
240 db = pa_xnew0(simple_data, 1);
241 db->map = pa_hashmap_new_full(hash_func, compare_func, NULL, (pa_free_cb_t) free_entry);
242 db->filename = pa_xstrdup(path);
243 db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
244 db->read_only = !for_write;
245
246 if (f) {
247 fill_data(db, f);
248 fclose(f);
249 }
250 } else {
251 if (errno == 0)
252 errno = EIO;
253 db = NULL;
254 }
255
256 return (pa_database*) db;
257 }
258
pa_database_close(pa_database * database)259 void pa_database_close(pa_database *database) {
260 simple_data *db = (simple_data*)database;
261 pa_assert(db);
262
263 pa_database_sync(database);
264 pa_xfree(db->filename);
265 pa_xfree(db->tmp_filename);
266 pa_hashmap_free(db->map);
267 pa_xfree(db);
268 }
269
pa_database_get(pa_database * database,const pa_datum * key,pa_datum * data)270 pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
271 simple_data *db = (simple_data*)database;
272 entry *e;
273
274 pa_assert(db);
275 pa_assert(key);
276 pa_assert(data);
277
278 e = pa_hashmap_get(db->map, key);
279
280 if (!e)
281 return NULL;
282
283 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
284 data->size = e->data.size;
285
286 return data;
287 }
288
pa_database_set(pa_database * database,const pa_datum * key,const pa_datum * data,bool overwrite)289 int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, bool overwrite) {
290 simple_data *db = (simple_data*)database;
291 entry *e;
292 int ret = 0;
293
294 pa_assert(db);
295 pa_assert(key);
296 pa_assert(data);
297
298 if (db->read_only)
299 return -1;
300
301 e = new_entry(key, data);
302
303 if (pa_hashmap_put(db->map, &e->key, e) < 0) {
304 /* entry with same key exists in hashmap */
305 entry *r;
306 if (overwrite) {
307 r = pa_hashmap_remove(db->map, key);
308 pa_hashmap_put(db->map, &e->key, e);
309 } else {
310 /* won't overwrite, so clean new entry */
311 r = e;
312 ret = -1;
313 }
314
315 free_entry(r);
316 }
317
318 return ret;
319 }
320
pa_database_unset(pa_database * database,const pa_datum * key)321 int pa_database_unset(pa_database *database, const pa_datum *key) {
322 simple_data *db = (simple_data*)database;
323
324 pa_assert(db);
325 pa_assert(key);
326
327 return pa_hashmap_remove_and_free(db->map, key);
328 }
329
pa_database_clear(pa_database * database)330 int pa_database_clear(pa_database *database) {
331 simple_data *db = (simple_data*)database;
332
333 pa_assert(db);
334
335 pa_hashmap_remove_all(db->map);
336
337 return 0;
338 }
339
pa_database_size(pa_database * database)340 signed pa_database_size(pa_database *database) {
341 simple_data *db = (simple_data*)database;
342 pa_assert(db);
343
344 return (signed) pa_hashmap_size(db->map);
345 }
346
pa_database_first(pa_database * database,pa_datum * key,pa_datum * data)347 pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
348 simple_data *db = (simple_data*)database;
349 entry *e;
350
351 pa_assert(db);
352 pa_assert(key);
353
354 e = pa_hashmap_first(db->map);
355
356 if (!e)
357 return NULL;
358
359 key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
360 key->size = e->key.size;
361
362 if (data) {
363 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
364 data->size = e->data.size;
365 }
366
367 return key;
368 }
369
pa_database_next(pa_database * database,const pa_datum * key,pa_datum * next,pa_datum * data)370 pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
371 simple_data *db = (simple_data*)database;
372 entry *e;
373 entry *search;
374 void *state;
375 bool pick_now;
376
377 pa_assert(db);
378 pa_assert(next);
379
380 if (!key)
381 return pa_database_first(database, next, data);
382
383 search = pa_hashmap_get(db->map, key);
384
385 state = NULL;
386 pick_now = false;
387
388 while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
389 if (pick_now)
390 break;
391
392 if (search == e)
393 pick_now = true;
394 }
395
396 if (!pick_now || !e)
397 return NULL;
398
399 next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
400 next->size = e->key.size;
401
402 if (data) {
403 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
404 data->size = e->data.size;
405 }
406
407 return next;
408 }
409
write_uint(FILE * f,const uint32_t num)410 static int write_uint(FILE *f, const uint32_t num) {
411 size_t items;
412 uint8_t values[4];
413 int i;
414 errno = 0;
415
416 for (i = 0; i < 4; i++)
417 values[i] = (num >> (i*8)) & 0xFF;
418
419 items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
420
421 if (ferror(f))
422 return -1;
423
424 return items;
425 }
426
write_data(FILE * f,void * data,const size_t length)427 static int write_data(FILE *f, void *data, const size_t length) {
428 size_t items;
429 uint32_t len;
430
431 len = length;
432 if ((items = write_uint(f, len)) <= 0)
433 return -1;
434
435 items = fwrite(data, length, 1, f);
436
437 if (ferror(f) || items != 1)
438 return -1;
439
440 return 0;
441 }
442
write_entry(FILE * f,const entry * e)443 static int write_entry(FILE *f, const entry *e) {
444 pa_assert(f);
445 pa_assert(e);
446
447 if (write_data(f, e->key.data, e->key.size) < 0)
448 return -1;
449 if (write_data(f, e->data.data, e->data.size) < 0)
450 return -1;
451
452 return 0;
453 }
454
pa_database_sync(pa_database * database)455 int pa_database_sync(pa_database *database) {
456 simple_data *db = (simple_data*)database;
457 FILE *f;
458 void *state;
459 entry *e;
460
461 pa_assert(db);
462
463 if (db->read_only)
464 return 0;
465
466 errno = 0;
467
468 f = pa_fopen_cloexec(db->tmp_filename, "w");
469
470 if (!f)
471 goto fail;
472
473 state = NULL;
474 while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
475 if (write_entry(f, e) < 0) {
476 pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
477 goto fail;
478 }
479 }
480
481 fclose(f);
482 f = NULL;
483
484 if (rename(db->tmp_filename, db->filename) < 0) {
485 pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
486 goto fail;
487 }
488
489 return 0;
490
491 fail:
492 if (f)
493 fclose(f);
494 return -1;
495 }
496