Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
tracker-data-backup.c:168:2 | clang-analyzer | Value stored to 'status' is never read |
1 /*
2 * Copyright (C) 2006, Jamie McCracken <jamiemcc@gnome.org>
3 * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20 #include "config.h"
21
22 #include <errno.h>
23 #include <string.h>
24
25 #include <glib.h>
26 #include <glib/gstdio.h>
27
28 #include <libtracker-common/tracker-os-dependant.h>
29
30 #include "tracker-data-backup.h"
31 #include "tracker-data-manager.h"
32 #include "tracker-db-manager.h"
33 #include "tracker-db-journal.h"
34 #include "tracker-db-backup.h"
35
36 typedef struct {
37 GFile *destination, *journal;
38 TrackerDataBackupFinished callback;
39 gpointer user_data;
40 GDestroyNotify destroy;
41 GError *error;
42 } BackupSaveInfo;
43
44 #ifndef DISABLE_JOURNAL
45
46 typedef struct {
47 GPid pid;
48 guint stdout_watch_id;
49 guint stderr_watch_id;
50 GIOChannel *stdin_channel;
51 GIOChannel *stdout_channel;
52 GIOChannel *stderr_channel;
53 gpointer data;
54 GString *lines;
55 } ProcessContext;
56
57 #endif /* DISABLE_JOURNAL */
58
59 static void
60 free_backup_save_info (BackupSaveInfo *info)
61 {
62 if (info->destination) {
63 g_object_unref (info->destination);
64 }
65
66 if (info->journal) {
67 g_object_unref (info->journal);
68 }
69
70 if (info->destroy) {
71 info->destroy (info->user_data);
72 }
73
74 g_clear_error (&info->error);
75
76 g_free (info);
77 }
78
79
80 GQuark
81 tracker_data_backup_error_quark (void)
82 {
83 return g_quark_from_static_string (TRACKER_DATA_BACKUP_ERROR_DOMAIN);
84 }
85
86 #ifndef DISABLE_JOURNAL
87
88 static void
89 on_journal_copied (BackupSaveInfo *info, GError *error)
90 {
91 if (info->callback) {
92 info->callback (error, info->user_data);
93 }
94
95 free_backup_save_info (info);
96 }
97
98
99
100 static void
101 process_context_destroy (ProcessContext *context, GError *error)
102 {
103 on_journal_copied (context->data, error);
104
105 if (context->lines) {
106 g_string_free (context->lines, TRUE);
107 }
108
109 if (context->stdin_channel) {
110 g_io_channel_shutdown (context->stdin_channel, FALSE, NULL);
111 g_io_channel_unref (context->stdin_channel);
112 context->stdin_channel = NULL;
113 }
114
115 if (context->stdout_watch_id != 0) {
116 g_source_remove (context->stdout_watch_id);
117 context->stdout_watch_id = 0;
118 }
119
120 if (context->stdout_channel) {
121 g_io_channel_shutdown (context->stdout_channel, FALSE, NULL);
122 g_io_channel_unref (context->stdout_channel);
123 context->stdout_channel = NULL;
124 }
125
126 if (context->stderr_watch_id != 0) {
127 g_source_remove (context->stderr_watch_id);
128 context->stderr_watch_id = 0;
129 }
130
131 if (context->stderr_channel) {
132 g_io_channel_shutdown (context->stderr_channel, FALSE, NULL);
133 g_io_channel_unref (context->stderr_channel);
134 context->stderr_channel = NULL;
135 }
136
137 if (context->pid != 0) {
138 g_spawn_close_pid (context->pid);
139 context->pid = 0;
140 }
141
142 g_free (context);
143 }
144
145 static gboolean
146 read_line_of_tar_output (GIOChannel *channel,
147 GIOCondition condition,
148 gpointer user_data)
149 {
150 if (condition & G_IO_ERR || condition & G_IO_HUP) {
151 return FALSE;
152 }
153
154 /* TODO: progress support */
155 return TRUE;
156 }
157
158 static gboolean
159 read_error_of_tar_output (GIOChannel *channel,
160 GIOCondition condition,
161 gpointer user_data)
162 {
163 ProcessContext *context;
164 GIOStatus status;
165 gchar *line;
166
167 context = user_data;
168 status = G_IO_STATUS_NORMAL;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
169
170 if (condition & G_IO_IN || condition & G_IO_PRI) {
171 do {
172 GError *error = NULL;
173
174 status = g_io_channel_read_line (channel, &line, NULL, NULL, &error);
175
176 if (status == G_IO_STATUS_NORMAL) {
177 if (context->lines == NULL)
178 context->lines = g_string_new (NULL);
179 g_string_append (context->lines, line);
180 g_free (line);
181 } else if (error) {
182 g_warning ("%s", error->message);
183 g_error_free (error);
184 }
185 } while (status == G_IO_STATUS_NORMAL);
186
187 if (status == G_IO_STATUS_EOF ||
188 status == G_IO_STATUS_ERROR) {
189 return FALSE;
190 }
191 }
192
193 if (condition & G_IO_ERR || condition & G_IO_HUP) {
194 return FALSE;
195 }
196
197 return TRUE;
198 }
199
200 static void
201 process_context_child_watch_cb (GPid pid,
202 gint status,
203 gpointer user_data)
204 {
205 ProcessContext *context;
206 GError *error = NULL;
207
208 g_debug ("Process '%d' exited with code %d", pid, status);
209
210 context = (ProcessContext *) user_data;
211
212 if (context->lines) {
213 g_set_error (&error, TRACKER_DATA_BACKUP_ERROR,
214 TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
215 "%s", context->lines->str);
216 }
217
218 process_context_destroy (context, error);
219 }
220 #endif /* DISABLE_JOURNAL */
221
222
223
224 #ifdef DISABLE_JOURNAL
225 static void
226 on_backup_finished (GError *error,
227 gpointer user_data)
228 {
229 BackupSaveInfo *info = user_data;
230
231 if (info->callback) {
232 info->callback (error, info->user_data);
233 }
234
235 free_backup_save_info (info);
236 }
237
238 #endif /* DISABLE_JOURNAL */
239
240 /* delete all regular files from the directory */
241 static void
242 dir_remove_files (const gchar *path)
243 {
244 GDir *dir;
245 const gchar *name;
246
247 dir = g_dir_open (path, 0, NULL);
248 if (dir == NULL) {
249 return;
250 }
251
252 while ((name = g_dir_read_name (dir)) != NULL) {
253 gchar *filename;
254
255 filename = g_build_filename (path, name, NULL);
256
257 if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
258 g_debug ("Removing '%s'", filename);
259 if (g_unlink (filename) == -1) {
260 g_warning ("Unable to remove '%s': %s", filename, g_strerror (errno));
261 }
262 }
263
264 g_free (filename);
265 }
266
267 g_dir_close (dir);
268 }
269
270 /* move all regular files from the source directory to the destination directory */
271 static void
272 dir_move_files (const gchar *src_path, const gchar *dest_path)
273 {
274 GDir *src_dir;
275 const gchar *src_name;
276
277 src_dir = g_dir_open (src_path, 0, NULL);
278 if (src_dir == NULL) {
279 return;
280 }
281
282 while ((src_name = g_dir_read_name (src_dir)) != NULL) {
283 gchar *src_filename, *dest_filename;
284
285 src_filename = g_build_filename (src_path, src_name, NULL);
286
287 if (g_file_test (src_filename, G_FILE_TEST_IS_REGULAR)) {
288 dest_filename = g_build_filename (dest_path, src_name, NULL);
289
290 g_debug ("Renaming '%s' to '%s'", src_filename, dest_filename);
291 if (g_rename (src_filename, dest_filename) == -1) {
292 g_warning ("Unable to rename '%s' to '%s': %s", src_filename, dest_filename, g_strerror (errno));
293 }
294
295 g_free (dest_filename);
296 }
297
298 g_free (src_filename);
299 }
300
301 g_dir_close (src_dir);
302 }
303
304 static void
305 dir_move_to_temp (const gchar *path)
306 {
307 gchar *temp_dir;
308
309 temp_dir = g_build_filename (path, "tmp", NULL);
310 g_mkdir (temp_dir, 0777);
311
312 /* ensure that no obsolete temporary files are around */
313 dir_remove_files (temp_dir);
314 dir_move_files (path, temp_dir);
315
316 g_free (temp_dir);
317 }
318
319 static void
320 dir_move_from_temp (const gchar *path)
321 {
322 gchar *temp_dir;
323
324 temp_dir = g_build_filename (path, "tmp", NULL);
325
326 /* ensure that no obsolete files are around */
327 dir_remove_files (path);
328 dir_move_files (temp_dir, path);
329
330 g_rmdir (temp_dir);
331
332 g_free (temp_dir);
333 }
334
335 static void
336 move_to_temp (void)
337 {
338 gchar *data_dir, *cache_dir;
339
340 g_message ("Moving all database files to temporary location");
341
342 data_dir = g_build_filename (g_get_user_data_dir (),
343 "tracker",
344 "data",
345 NULL);
346
347 cache_dir = g_build_filename (g_get_user_cache_dir (),
348 "tracker",
349 NULL);
350
351 dir_move_to_temp (data_dir);
352 dir_move_to_temp (cache_dir);
353
354 g_free (cache_dir);
355 g_free (data_dir);
356 }
357
358 static void
359 remove_temp (void)
360 {
361 gchar *tmp_data_dir, *tmp_cache_dir;
362
363 g_message ("Removing all database files from temporary location");
364
365 tmp_data_dir = g_build_filename (g_get_user_data_dir (),
366 "tracker",
367 "data",
368 "tmp",
369 NULL);
370
371 tmp_cache_dir = g_build_filename (g_get_user_cache_dir (),
372 "tracker",
373 "tmp",
374 NULL);
375
376 dir_remove_files (tmp_data_dir);
377 dir_remove_files (tmp_cache_dir);
378
379 g_rmdir (tmp_data_dir);
380 g_rmdir (tmp_cache_dir);
381
382 g_free (tmp_cache_dir);
383 g_free (tmp_data_dir);
384 }
385
386 static void
387 restore_from_temp (void)
388 {
389 gchar *data_dir, *cache_dir;
390
391 g_message ("Restoring all database files from temporary location");
392
393 data_dir = g_build_filename (g_get_user_data_dir (),
394 "tracker",
395 "data",
396 NULL);
397
398 cache_dir = g_build_filename (g_get_user_cache_dir (),
399 "tracker",
400 NULL);
401
402 dir_move_from_temp (data_dir);
403 dir_move_from_temp (cache_dir);
404
405 g_free (cache_dir);
406 g_free (data_dir);
407 }
408
409 void
410 tracker_data_backup_save (GFile *destination,
411 TrackerDataBackupFinished callback,
412 gpointer user_data,
413 GDestroyNotify destroy)
414 {
415 #ifndef DISABLE_JOURNAL
416 BackupSaveInfo *info;
417 ProcessContext *context;
418 gchar **argv;
419 gchar *path, *directory;
420 GDir *journal_dir;
421 GFile *parent;
422 GIOChannel *stdin_channel, *stdout_channel, *stderr_channel;
423 GPid pid;
424 GPtrArray *files;
425 const gchar *f_name;
426 guint i;
427
428 info = g_new0 (BackupSaveInfo, 1);
429 info->destination = g_object_ref (destination);
430 info->journal = g_file_new_for_path (tracker_db_journal_get_filename ());
431 info->callback = callback;
432 info->user_data = user_data;
433 info->destroy = destroy;
434
435 parent = g_file_get_parent (info->journal);
436 directory = g_file_get_path (parent);
437 g_object_unref (parent);
438 path = g_file_get_path (destination);
439
440 journal_dir = g_dir_open (directory, 0, NULL);
441 f_name = g_dir_read_name (journal_dir);
442 files = g_ptr_array_new ();
443
444 while (f_name) {
445 if (f_name) {
446 if (!g_str_has_prefix (f_name, TRACKER_DB_JOURNAL_FILENAME ".")) {
447 f_name = g_dir_read_name (journal_dir);
448 continue;
449 }
450 g_ptr_array_add (files, g_strdup (f_name));
451 }
452 f_name = g_dir_read_name (journal_dir);
453 }
454
455 g_dir_close (journal_dir);
456
457 argv = g_new0 (gchar*, files->len + 8);
458
459 argv[0] = g_strdup ("tar");
460 argv[1] = g_strdup ("-zcf");
461 argv[2] = path;
462 argv[3] = g_strdup ("-C");
463 argv[4] = directory;
464 argv[5] = g_strdup (TRACKER_DB_JOURNAL_FILENAME);
465 argv[6] = g_strdup (TRACKER_DB_JOURNAL_ONTOLOGY_FILENAME);
466
467 for (i = 0; i < files->len; i++) {
468 argv[i+7] = g_ptr_array_index (files, i);
469 }
470
471 /* It's fine to untar this asynchronous: the journal replay code can or
472 * should cope with unfinished entries at the end of the file, while
473 * restoring a backup made this way. */
474
475 if (!tracker_spawn_async_with_channels ((const gchar **) argv,
476 0, &pid,
477 &stdin_channel,
478 &stdout_channel,
479 &stderr_channel)) {
480 GError *error = NULL;
481 g_set_error (&error, TRACKER_DATA_BACKUP_ERROR,
482 TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
483 "Error starting tar program");
484 on_journal_copied (info, error);
485 g_strfreev (argv);
486 g_error_free (error);
487 return;
488 }
489
490 context = g_new0 (ProcessContext, 1);
491 context->lines = NULL;
492 context->data = info;
493 context->pid = pid;
494 context->stdin_channel = stdin_channel;
495 context->stderr_channel = stderr_channel;
496 context->stdout_watch_id = g_io_add_watch (stdout_channel,
497 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
498 read_line_of_tar_output,
499 context);
500 context->stderr_watch_id = g_io_add_watch (stderr_channel,
501 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
502 read_error_of_tar_output,
503 context);
504
505 g_child_watch_add (context->pid, process_context_child_watch_cb, context);
506
507 g_debug ("Process '%d' spawned for command:'%s %s %s'",
508 pid, argv[0], argv[1], argv[2]);
509
510 g_strfreev (argv);
511 #else
512 BackupSaveInfo *info;
513
514 info = g_new0 (BackupSaveInfo, 1);
515 info->destination = g_object_ref (destination);
516 info->callback = callback;
517 info->user_data = user_data;
518 info->destroy = destroy;
519
520 tracker_db_backup_save (destination,
521 on_backup_finished,
522 info,
523 NULL);
524 #endif /* DISABLE_JOURNAL */
525 }
526
527 void
528 tracker_data_backup_restore (GFile *journal,
529 const gchar **test_schemas,
530 TrackerBusyCallback busy_callback,
531 gpointer busy_user_data,
532 GError **error)
533 {
534 BackupSaveInfo *info;
535 GError *internal_error = NULL;
536
537 info = g_new0 (BackupSaveInfo, 1);
538 #ifndef DISABLE_JOURNAL
539 info->destination = g_file_new_for_path (tracker_db_journal_get_filename ());
540 #else
541 info->destination = g_file_new_for_path (tracker_db_manager_get_file (TRACKER_DB_METADATA));
542 #endif /* DISABLE_JOURNAL */
543
544 info->journal = g_object_ref (journal);
545
546 if (g_file_query_exists (info->journal, NULL)) {
547 TrackerDBManagerFlags flags;
548 guint select_cache_size, update_cache_size;
549 gboolean is_first;
550 #ifndef DISABLE_JOURNAL
551 GError *n_error = NULL;
552 GFile *parent = g_file_get_parent (info->destination);
553 gchar *tmp_stdout = NULL;
554 gchar *tmp_stderr = NULL;
555 gchar **argv;
556 gint exit_status;
557 #endif /* DISABLE_JOURNAL */
558
559 flags = tracker_db_manager_get_flags (&select_cache_size, &update_cache_size);
560
561 tracker_data_manager_shutdown ();
562
563 move_to_temp ();
564
565 #ifndef DISABLE_JOURNAL
566 argv = g_new0 (char*, 6);
567
568 argv[0] = g_strdup ("tar");
569 argv[1] = g_strdup ("-zxf");
570 argv[2] = g_file_get_path (info->journal);
571 argv[3] = g_strdup ("-C");
572 argv[4] = g_file_get_path (parent);
573
574 g_object_unref (parent);
575
576 /* Synchronous: we don't want the mainloop to run while copying the
577 * journal, as nobody should be writing anything at this point */
578
579 if (!tracker_spawn (argv, 0, &tmp_stdout, &tmp_stderr, &exit_status)) {
580 g_set_error (&info->error, TRACKER_DATA_BACKUP_ERROR,
581 TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
582 "Error starting tar program");
583 }
584
585 if (!info->error && tmp_stderr) {
586 if (strlen (tmp_stderr) > 0) {
587 g_set_error (&info->error, TRACKER_DATA_BACKUP_ERROR,
588 TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
589 "%s", tmp_stderr);
590 }
591 }
592
593 if (!info->error && exit_status != 0) {
594 g_set_error (&info->error, TRACKER_DATA_BACKUP_ERROR,
595 TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
596 "Unknown error, tar exited with exit status %d", exit_status);
597 }
598
599 g_free (tmp_stderr);
600 g_free (tmp_stdout);
601 g_strfreev (argv);
602 #else
603 /* Turn off force-reindex here, no journal to replay so it wouldn't work */
604 flags &= ~TRACKER_DB_MANAGER_FORCE_REINDEX;
605
606 g_file_copy (info->journal, info->destination,
607 G_FILE_COPY_OVERWRITE,
608 NULL, NULL, NULL,
609 &info->error);
610 #endif /* DISABLE_JOURNAL */
611
612 tracker_db_manager_init_locations ();
613
614 /* Re-set the DB version file, so that its mtime changes. The mtime of this
615 * file will change only when the whole DB is recreated (after a hard reset
616 * or after a backup restoration). */
617 tracker_db_manager_create_version_file ();
618
619 /* Given we're radically changing the database, we
620 * force a full mtime check against all known files in
621 * the database for complete synchronisation. */
622 tracker_db_manager_set_need_mtime_check (TRUE);
623
624 #ifndef DISABLE_JOURNAL
625 tracker_db_journal_init (NULL, FALSE, &n_error);
626
627 if (n_error) {
628 if (!info->error) {
629 g_propagate_error (&info->error, n_error);
630 } else {
631 g_warning ("Ignored error while initializing journal during backup (another higher priority error already took place): %s",
632 n_error->message ? n_error->message : "No error given");
633 g_error_free (n_error);
634 }
635 n_error = NULL;
636 }
637
638 if (info->error) {
639 restore_from_temp ();
640 } else {
641 remove_temp ();
642 }
643
644 tracker_db_journal_shutdown (&n_error);
645
646 if (n_error) {
647 g_warning ("Ignored error while shuting down journal during backup: %s",
648 n_error->message ? n_error->message : "No error given");
649 g_error_free (n_error);
650 }
651 #endif /* DISABLE_JOURNAL */
652
653 tracker_data_manager_init (flags, test_schemas, &is_first, TRUE, TRUE,
654 select_cache_size, update_cache_size,
655 busy_callback, busy_user_data,
656 "Restoring backup", &internal_error);
657
658 #ifdef DISABLE_JOURNAL
659 if (internal_error) {
660 restore_from_temp ();
661
662 tracker_data_manager_init (flags, test_schemas, &is_first, TRUE, TRUE,
663 select_cache_size, update_cache_size,
664 busy_callback, busy_user_data,
665 "Restoring backup", &internal_error);
666 } else {
667 remove_temp ();
668 }
669 #endif /* DISABLE_JOURNAL */
670
671 if (internal_error) {
672 g_propagate_error (error, internal_error);
673 }
674 } else {
675 g_set_error (&info->error, TRACKER_DATA_BACKUP_ERROR,
676 TRACKER_DATA_BACKUP_ERROR_UNKNOWN,
677 "Backup file doesn't exist");
678 }
679
680 if (info->error) {
681 g_propagate_error (error, info->error);
682 info->error = NULL;
683 }
684
685 free_backup_save_info (info);
686 }