Bug Summary

File:build-analysis/../src/lib/s4/src/lib/s4.c
Warning:line 305, column 16
Assigned value is garbage or undefined

Annotated Source Code

1/* S4 - An XMMS2 medialib backend
2 * Copyright (C) 2009, 2010 Sivert Berg
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 */
14
15#include <s4.h>
16#include "s4_priv.h"
17#include "logging.h"
18#include <string.h>
19#include <stdlib.h>
20#include <glib/gstdio.h>
21#include <errno(*__errno_location ()).h>
22
23static GPrivate _errno = G_PRIVATE_INIT (g_free){ ((void*)0), (g_free), { ((void*)0), ((void*)0) } };
24
25/**
26 *
27 * @defgroup S4 S4
28 * @brief A database backend for XMMS2
29 *
30 * @{
31 */
32
33#define S4_MAGIC("s4db") ("s4db")
34#define S4_MAGIC_LEN(4) (4)
35#define S4_VERSION1 1
36
37typedef struct {
38 char magic[S4_MAGIC_LEN(4)];
39 int32_t version;
40 unsigned char uuid[16];
41 log_number_t last_checkpoint;
42} s4_header_t;
43
44/**
45 * @{
46 * @internal
47 */
48
49/**
50 * Reads strings from a file
51 *
52 * @param s4 The database to add the strings to
53 * @param file The file to read from
54 * @return A hashtable with the id as they key and the
55 * corresponding string as they values or NULL on error
56 */
57static GHashTable *_read_string (s4_t *s4, FILE *file)
58{
59 size_t r;
60 int32_t id, len;
61 char *str;
62 GHashTable *ret = g_hash_table_new (NULL((void*)0), NULL((void*)0));
63
64 while ((r = fread (&id, sizeof (int32_t), 1, file)) == 1 &&
65 id != -1 &&
66 (r = fread (&len, sizeof (int32_t), 1, file)) == 1) {
67 str = malloc (len + 1);
68 r = fread (str, 1, len, file);
69
70 if (r != len) {
71 g_hash_table_destroy (ret);
72 return NULL((void*)0);
73 }
74
75 str[len] = '\0';
76 g_hash_table_insert (ret, GINT_TO_POINTER (id)((gpointer) (glong) (id)), (void*)_string_lookup (s4, str));
77 free (str);
78 }
79
80 if (r == 0) {
81 g_hash_table_destroy (ret);
82 return NULL((void*)0);
83 }
84
85 return ret;
86}
87
88/**
89 * Reads relations from a file
90 *
91 * @param s4 The database to insert them into
92 * @param file The file to read
93 * @param strings A hashtable with string->int relationships
94 * @return -1 on error, 0 otherwise
95 */
96static int _read_relations (s4_t *s4, FILE *file, GHashTable *strings)
97{
98 s4_intpair_t rec;
99
100 while (fread (&rec, sizeof (s4_intpair_t), 1, file) == 1) {
101 const char *key_a, *key_b, *src;
102 const s4_val_t *val_a, *val_b;
103
104 key_a = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.key_a))((gpointer) (glong) ((((rec.key_a) < 0) ? -(rec.key_a) : (
rec.key_a))))
);
105 key_b = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.key_b))((gpointer) (glong) ((((rec.key_b) < 0) ? -(rec.key_b) : (
rec.key_b))))
);
106 src = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.src))((gpointer) (glong) ((((rec.src) < 0) ? -(rec.src) : (rec.
src))))
);
107
108 if (rec.key_a > 0) {
109 val_a = _string_lookup_val (s4,
110 g_hash_table_lookup (strings, GINT_TO_POINTER (rec.val_a)((gpointer) (glong) (rec.val_a))));
111 } else {
112 val_a = _int_lookup_val (s4, rec.val_a);
113 }
114 if (rec.key_b > 0) {
115 val_b = _string_lookup_val (s4,
116 g_hash_table_lookup (strings, GINT_TO_POINTER (rec.val_b)((gpointer) (glong) (rec.val_b))));
117 } else {
118 val_b = _int_lookup_val (s4, rec.val_b);
119 }
120
121 _s4_add_internal (s4, key_a, val_a, key_b, val_b, src);
122 }
123
124 return 0;
125}
126
127/**
128 * Reads an S4 database from filename.
129 *
130 * @param s4 The s4 database to read the data into
131 * @param filename The name of the file to read from
132 * @param flags Flags passed to s4_open
133 * @return 0 on success, non-zero on error
134 */
135static int _read_file (s4_t *s4, const char *filename, int flags)
136{
137 FILE *file = fopen (filename, "r");
138 s4_header_t hdr;
139 int i;
140
141 if (file == NULL((void*)0)) {
142 int ret = 0;
143 switch (errno(*__errno_location ())) {
144 case ENOENT2:
145 if (flags & S4_EXISTS) {
146 s4_set_errno (S4E_NOENT);
147 ret = -1;
148 } else {
149 s4_create_uuid (s4->uuid);
150 }
151 break;
152 default:
153 s4_set_errno (S4E_OPEN);
154 ret = -1;
155 break;
156 }
157 return ret;
158 } else if (flags & S4_NEW) {
159 fclose (file);
160 s4_set_errno (S4E_EXISTS);
161 return -1;
162 }
163
164 fread (&hdr, sizeof (s4_header_t), 1, file);
165 if (strncmp (S4_MAGIC("s4db"), hdr.magic, S4_MAGIC_LEN(4))) {
166 fclose (file);
167 s4_set_errno (S4E_MAGIC);
168 return -1;
169 }
170
171 if (hdr.version != S4_VERSION1) {
172 fclose (file);
173 s4_set_errno (S4E_VERSION);
174 return -1;
175 }
176
177 _log_init (s4, hdr.last_checkpoint);
178
179 for (i = 0; i < 16; i++) {
180 s4->uuid[i] = hdr.uuid[i];
181 }
182
183 GHashTable *strings = _read_string (s4, file);
184 if (strings == NULL((void*)0) || _read_relations (s4, file, strings) == -1) {
185 fclose (file);
186 s4_set_errno (S4E_INCONS);
187 return -1;
188 }
189 g_hash_table_destroy (strings);
190
191 fclose (file);
192 return 0;
193}
194
195int _reread_file (s4_t *s4)
196{
197 _free_relations (s4);
198
199 _index_free_data (s4->index_data);
200 _entry_free_data (s4->entry_data);
201 s4->index_data = _index_create_data ();
202 s4->entry_data = _entry_create_data ();
203
204 return _read_file (s4, s4->filename, S4_EXISTS);
205}
206
207/**
208 * Writes all the id->string relations in the hash table to file
209 *
210 * @param strings The hashtable holding the key-value pairs
211 * @param file The file to write to
212 */
213static void _write_strings (GHashTable *strings, FILE *file)
214{
215 GHashTableIter iter;
216 void *i;
217 const char *str;
218
219 g_hash_table_iter_init (&iter, strings);
220 while (g_hash_table_iter_next (&iter, (void**)&str, &i)) {
221 int32_t len = strlen (str);
222 int32_t str_id = GPOINTER_TO_INT (i)((gint) (glong) (i));
223 fwrite (&str_id, sizeof (int32_t), 1, file);
224 fwrite (&len, sizeof (int32_t), 1, file);
225 fwrite (str, 1, len, file);
226 }
227}
228
229/**
230 * Writes a list of int-pairs to file
231 *
232 * @param pairs A GList with pairs to write
233 * @param file The file to write to
234 */
235static void _write_pairs (GList *pairs, FILE *file)
236{
237 for (; pairs != NULL((void*)0); pairs = g_list_next (pairs)((pairs) ? (((GList *)(pairs))->next) : ((void*)0))) {
238 s4_intpair_t *pair = pairs->data;
239 fwrite (pair, sizeof (s4_intpair_t), 1, file);
240 free (pair);
241 }
242}
243
244typedef struct {
245 GHashTable *strings;
246 GList *pairs;
247 int new_id;
248} save_data_t;
249
250/**
251 * Gets the id of a string, or gives it an unique id if it doesn't have one
252 *
253 * @param sd A structure holding the strings found so far and the next free id.
254 * @param str The string to lookup
255 * @return The id associated with the string
256 */
257static int _get_string_number (save_data_t *sd, const char *str)
258{
259 int i = GPOINTER_TO_INT (g_hash_table_lookup (sd->strings, str))((gint) (glong) (g_hash_table_lookup (sd->strings, str)));
260
261 if (i == 0) {
262 i = sd->new_id++;
263 g_hash_table_insert (sd->strings, (void*)str, GINT_TO_POINTER (i)((gpointer) (glong) (i)));
264 }
265
266 return i;
267}
268
269/*
270 * A helper function converting a resultset into a list of int-pairs.
271 */
272static void _result_to_pairs (s4_resultset_t *res, save_data_t *sd)
273{
274 const s4_resultrow_t *row;
275 int row_no;
276
277 for (row_no = 0; s4_resultset_get_row (res, row_no, &row); row_no++) {
10
Loop condition is true. Entering loop body
17
Loop condition is true. Entering loop body
278 int32_t va, ka, i;
18
'va' declared without an initial value
279 const s4_val_t *val_a, *val_b;
280 const char *key_a, *key_b, *src, *str;
281 const s4_result_t *id_res, *val_res;
282
283 s4_resultrow_get_col (row, 0, &id_res);
284 s4_resultrow_get_col (row, 1, &val_res);
285
286 val_a = s4_result_get_val (id_res);
287 key_a = s4_result_get_key (id_res);
288
289 ka = _get_string_number (sd, key_a);
290
291 if (s4_val_get_int (val_a, &i)) {
11
Assuming the condition is false
12
Taking false branch
19
Assuming the condition is false
20
Taking false branch
292 va = i;
293 ka = -ka;
294 } else if (s4_val_get_str (val_a, &str)) {
13
Assuming the condition is false
14
Taking false branch
21
Assuming the condition is false
22
Taking false branch
295 va = _get_string_number (sd, str);
296 }
297
298 for (; val_res != NULL((void*)0); val_res = s4_result_next (val_res)) {
15
Assuming 'val_res' is equal to NULL
16
Loop condition is false. Execution continues on line 277
23
Assuming 'val_res' is not equal to NULL
24
Loop condition is true. Entering loop body
299 s4_intpair_t *pair = malloc (sizeof (s4_intpair_t));
300
301 val_b = s4_result_get_val (val_res);
302 key_b = s4_result_get_key (val_res);
303 src = s4_result_get_src (val_res);
304
305 pair->val_a = va;
25
Assigned value is garbage or undefined
306 pair->key_a = ka;
307 pair->key_b = _get_string_number (sd, key_b);
308 pair->src = _get_string_number (sd, src);
309
310 if (s4_val_get_int (val_b, &i)) {
311 pair->val_b = i;
312 pair->key_b = -pair->key_b;
313 } else if (s4_val_get_str (val_b, &str)) {
314 pair->val_b = _get_string_number (sd, str);
315 }
316
317 sd->pairs = g_list_prepend (sd->pairs, pair);
318 }
319 }
320}
321
322/**
323 * Writes the database to disk
324 *
325 * @param s4 The database to write
326 * @return non-zero on success, 0 on error
327 */
328static int _write_file (s4_t *s4)
329{
330 int32_t i = -1;
331 int j;
332 FILE *file;
333 s4_header_t hdr;
334 save_data_t sd;
335 s4_condition_t *cond;
336 s4_fetchspec_t *fs;
337 s4_resultset_t *res;
338 s4_transaction_t *trans;
339
340 _log_lock_db (s4);
341
342 file = fopen (s4->tmp_filename, "w");
343 if (file == NULL((void*)0)) {
5
Assuming 'file' is not equal to NULL
6
Taking false branch
344 _log_unlock_db (s4);
345 return 0;
346 }
347
348 sd.strings = g_hash_table_new (NULL((void*)0), NULL((void*)0));
349 sd.pairs = NULL((void*)0);
350 sd.new_id = 1;
351
352 cond = s4_cond_new_filter (S4_FILTER_EXISTS, NULL((void*)0), NULL((void*)0), NULL((void*)0), S4_CMP_BINARY, 0);
353
354 fs = s4_fetchspec_create ();
355 s4_fetchspec_add (fs, NULL((void*)0), NULL((void*)0), S4_FETCH_PARENT1);
356 s4_fetchspec_add (fs, NULL((void*)0), NULL((void*)0), S4_FETCH_DATA2);
357
358 do {
8
Loop condition is false. Exiting loop
359 trans = s4_begin (s4, 0);
360 res = s4_query (trans, fs, cond);
361 _transaction_writing (trans);
362 } while (!s4_commit (trans));
7
Assuming the condition is false
363
364 _result_to_pairs (res, &sd);
9
Calling '_result_to_pairs'
365
366 s4_cond_free (cond);
367 s4_fetchspec_free (fs);
368 s4_resultset_free (res);
369
370 strncpy (hdr.magic, S4_MAGIC("s4db"), S4_MAGIC_LEN(4));
371 hdr.version = S4_VERSION1;
372 for (j = 0; j < 16; j++) {
373 hdr.uuid[j] = s4->uuid[j];
374 }
375 hdr.last_checkpoint = _log_last_synced (s4);
376
377 fwrite (&hdr, sizeof (s4_header_t), 1, file);
378 _write_strings (sd.strings, file);
379 fwrite (&i, sizeof (int32_t), 1, file);
380 _write_pairs (sd.pairs, file);
381
382 g_hash_table_destroy (sd.strings);
383 g_list_free (sd.pairs);
384
385 fclose (file);
386
387 g_renamerename (s4->tmp_filename, s4->filename);
388
389 _log_checkpoint (s4);
390 _log_unlock_db (s4);
391 return 1;
392}
393
394static void *_sync_thread (s4_t *s4)
395{
396 g_mutex_lock (&s4->sync_lock);
397 while (s4->sync_thread_run) {
1
Loop condition is true. Entering loop body
398 if (s4->sync_thread_run)
2
Taking true branch
399 g_cond_wait (&s4->sync_cond, &s4->sync_lock);
400 g_mutex_unlock (&s4->sync_lock);
401
402 s4_sync (s4);
3
Calling 's4_sync'
403
404 g_mutex_lock (&s4->sync_lock);
405 g_cond_broadcast (&s4->sync_finished_cond);
406 }
407 g_mutex_unlock (&s4->sync_lock);
408
409 return NULL((void*)0);
410}
411
412/* Start sync */
413void _start_sync (s4_t *s4)
414{
415 g_mutex_lock (&s4->sync_lock);
416 g_cond_signal (&s4->sync_cond);
417 g_mutex_unlock (&s4->sync_lock);
418}
419
420/* Start and wait for sync to finish */
421void _sync (s4_t *s4)
422{
423 g_mutex_lock (&s4->sync_lock);
424 g_cond_signal (&s4->sync_cond);
425 g_cond_wait (&s4->sync_finished_cond, &s4->sync_lock);
426 g_mutex_unlock (&s4->sync_lock);
427}
428
429static s4_t *_alloc (void)
430{
431 s4_t* s4 = calloc (1, sizeof(s4_t));
432
433 g_mutex_init (&s4->sync_lock);
434 g_cond_init (&s4->sync_cond);
435 g_cond_init (&s4->sync_finished_cond);
436
437 s4->const_data = _const_create_data ();
438 s4->index_data = _index_create_data ();
439 s4->entry_data = _entry_create_data ();
440 s4->log_data = _log_create_data ();
441
442 return s4;
443}
444
445/**
446 * Frees an S4 handle and everything it points at
447 *
448 * @param s4 The handle to free
449 */
450static void _free (s4_t *s4)
451{
452 _free_relations (s4);
453
454 g_mutex_clear (&s4->sync_lock);
455 g_cond_clear (&s4->sync_cond);
456 g_cond_clear (&s4->sync_finished_cond);
457
458 _const_free_data (s4->const_data);
459 _index_free_data (s4->index_data);
460 _entry_free_data (s4->entry_data);
461 _log_free_data (s4->log_data);
462
463 free (s4->filename);
464 g_free (s4->tmp_filename);
465 free (s4);
466}
467
468/**
469 * @}
470 */
471
472/**
473 * Opens an S4 database
474 *
475 * @b The different flags you can pass:
476 * <P>
477 * @b S4_NEW
478 * <BR>
479 * It will create a new file if one does not already exists.
480 * If one exists it will fail and return NULL.
481 * </P><P>
482 * @b S4_EXISTS
483 * <BR>
484 * If the file does not exists it will fail and return NULL.
485 * s4_errno may be used to get more information about what
486 * went wrong.
487 * </P><P>
488 * @b S4_MEMORY
489 * Creates a memory-only database. It will not read any files
490 * on startup or write files on shutdown. Use this if you want
491 * a temporary database.
492 * <BR>
493 *
494 * @param filename The name of the file containing the database
495 * @param indices An array of keys to have indices on
496 * @param open_flags Zero or more of the flags bitwise-or'd.
497 * @return A pointer to an s4_t, or NULL if something went wrong.
498 */
499s4_t *s4_open (const char *filename, const char **indices, int open_flags)
500{
501 int i;
502 s4_t *s4;
503
504 s4 = _alloc ();
505
506 for (i = 0; indices != NULL((void*)0) && indices[i] != NULL((void*)0); i++) {
507 _index_add (s4, indices[i], _index_create ());
508 }
509
510 s4->open_flags = open_flags;
511
512 if (open_flags & S4_MEMORY) {
513 return s4;
514 }
515
516 s4->filename = strdup (filename);
517 s4->tmp_filename = g_strconcat (filename, ".chkpnt", NULL((void*)0));
518 if (_read_file (s4, s4->filename, open_flags)) {
519 _free (s4);
520 return NULL((void*)0);
521 }
522
523 if (!_log_open (s4)) {
524 _free (s4);
525 return NULL((void*)0);
526 }
527
528 /* Write the file right away in case we have opened a new file
529 * or we redid something in the log that was not in the file
530 */
531 s4_sync (s4);
532
533 s4->sync_thread_run = 1;
534 s4->sync_thread = g_thread_new ("s4 sync", (GThreadFunc)_sync_thread, s4);
535
536 return s4;
537}
538
539/**
540 * Closes an open S4 database
541 *
542 * @param s4 The database to close
543 *
544 */
545int s4_close (s4_t* s4)
546{
547 if (!(s4->open_flags & S4_MEMORY)) {
548 g_mutex_lock (&s4->sync_lock);
549 s4->sync_thread_run = 0;
550 g_cond_signal (&s4->sync_cond);
551 g_mutex_unlock (&s4->sync_lock);
552 g_thread_join (s4->sync_thread);
553
554 _log_close (s4);
555 }
556
557 _free (s4);
558
559 return 0;
560}
561
562
563/**
564 * Writes all changes to disk
565 *
566 * @param s4 The database to sync
567 *
568 */
569void s4_sync (s4_t *s4)
570{
571 if (!_write_file (s4)) {
4
Calling '_write_file'
572 S4_ERROR ("s4_sync: could not write file")g_warning ("../src/lib/s4/src/lib/s4.c" ":" "572" ": " "s4_sync: could not write file"
)
;
573 }
574}
575
576/**
577 * Returns the last error number set.
578 * This function is thread safe, error numbers set in one thread
579 * will NOT be seen in another thread.
580 *
581 * @return The last error number set, or S4E_NOERROR if none has been set
582 */
583s4_errno_t s4_errno()
584{
585 s4_errno_t *i = g_private_get (&_errno);
586 if (i == NULL((void*)0)) {
587 return S4E_NOERROR;
588 }
589 return *i;
590}
591
592/**
593 * Sets errno to the given error number
594 *
595 * @param err The error number to set
596 */
597void s4_set_errno (s4_errno_t err)
598{
599 s4_errno_t *i = g_private_get (&_errno);
600 if (i == NULL((void*)0)) {
601 i = malloc (sizeof (s4_errno_t));
602 g_private_set (&_errno, i);
603 }
604
605 *i = err;
606}
607
608/**
609 * @}
610 */