• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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