tracker-0.16.2/src/libtracker-data/tracker-data-backup.c

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;
Value stored to 'status' is never read
(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 }