tracker-0.16.2/src/libtracker-data/tracker-db-manager.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found tracker-db-manager.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
   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  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the
  16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17  * Boston, MA  02110-1301, USA.
  18  */
  19 
  20 #include "config.h"
  21 
  22 #include <string.h>
  23 #include <stdlib.h>
  24 #include <regex.h>
  25 #include <zlib.h>
  26 #include <locale.h>
  27 #include <time.h>
  28 #include <unistd.h>
  29 #include <sys/types.h>
  30 #include <stdio.h>
  31 #include <fcntl.h>
  32 #include <errno.h>
  33 
  34 #include <glib/gstdio.h>
  35 
  36 #include <libtracker-common/tracker-date-time.h>
  37 #include <libtracker-common/tracker-file-utils.h>
  38 #include <libtracker-common/tracker-utils.h>
  39 #include <libtracker-common/tracker-locale.h>
  40 
  41 #if HAVE_TRACKER_FTS
  42 #include <libtracker-fts/tracker-fts.h>
  43 #endif
  44 
  45 #include "tracker-db-journal.h"
  46 #include "tracker-db-manager.h"
  47 #include "tracker-db-interface-sqlite.h"
  48 #include "tracker-db-interface.h"
  49 #include "tracker-data-manager.h"
  50 
  51 /* ZLib buffer settings */
  52 #define ZLIB_BUF_SIZE                 8192
  53 
  54 /* Required minimum space needed to create databases (5Mb) */
  55 #define TRACKER_DB_MIN_REQUIRED_SPACE 5242880
  56 
  57 /* Default memory settings for databases */
  58 #define TRACKER_DB_PAGE_SIZE_DONT_SET -1
  59 
  60 /* Set current database version we are working with */
  61 #define TRACKER_DB_VERSION_NOW        TRACKER_DB_VERSION_0_15_2
  62 #define TRACKER_DB_VERSION_FILE       "db-version.txt"
  63 #define TRACKER_DB_LOCALE_FILE        "db-locale.txt"
  64 
  65 #define IN_USE_FILENAME               ".meta.isrunning"
  66 
  67 /* Stamp files to know crawling/indexing state */
  68 #define FIRST_INDEX_FILENAME          "first-index.txt"
  69 #define LAST_CRAWL_FILENAME           "last-crawl.txt"
  70 #define NEED_MTIME_CHECK_FILENAME     "no-need-mtime-check.txt"
  71 
  72 typedef enum {
  73 	TRACKER_DB_LOCATION_DATA_DIR,
  74 	TRACKER_DB_LOCATION_USER_DATA_DIR,
  75 } TrackerDBLocation;
  76 
  77 typedef enum {
  78 	TRACKER_DB_VERSION_UNKNOWN, /* Unknown */
  79 	TRACKER_DB_VERSION_0_6_6,   /* before indexer-split */
  80 	TRACKER_DB_VERSION_0_6_90,  /* after  indexer-split */
  81 	TRACKER_DB_VERSION_0_6_91,  /* stable release */
  82 	TRACKER_DB_VERSION_0_6_92,  /* current TRUNK */
  83 	TRACKER_DB_VERSION_0_7_0,   /* vstore branch */
  84 	TRACKER_DB_VERSION_0_7_4,   /* nothing special */
  85 	TRACKER_DB_VERSION_0_7_12,  /* nmo ontology */
  86 	TRACKER_DB_VERSION_0_7_13,  /* coalesce & writeback */
  87 	TRACKER_DB_VERSION_0_7_17,  /* mlo ontology */
  88 	TRACKER_DB_VERSION_0_7_20,  /* nco im ontology */
  89 	TRACKER_DB_VERSION_0_7_21,  /* named graphs/localtime */
  90 	TRACKER_DB_VERSION_0_7_22,  /* fts-limits branch */
  91 	TRACKER_DB_VERSION_0_7_28,  /* RC1 + mto + nco:url */
  92 	TRACKER_DB_VERSION_0_8_0,   /* stable release */
  93 	TRACKER_DB_VERSION_0_9_0,   /* unstable release */
  94 	TRACKER_DB_VERSION_0_9_8,   /* affiliation cardinality + volumes */
  95 	TRACKER_DB_VERSION_0_9_15,  /* mtp:hidden */
  96 	TRACKER_DB_VERSION_0_9_16,  /* Fix for NB#184823 */
  97 	TRACKER_DB_VERSION_0_9_19,  /* collation */
  98 	TRACKER_DB_VERSION_0_9_21,  /* Fix for NB#186055 */
  99 	TRACKER_DB_VERSION_0_9_24,  /* nmo:PhoneMessage class */
 100 	TRACKER_DB_VERSION_0_9_34,  /* ontology cache */
 101 	TRACKER_DB_VERSION_0_9_38,  /* nie:url an inverse functional property */
 102 	TRACKER_DB_VERSION_0_15_2   /* fts4 */
 103 } TrackerDBVersion;
 104 
 105 typedef struct {
 106 	TrackerDB           db;
 107 	TrackerDBLocation   location;
 108 	TrackerDBInterface *iface;
 109 	const gchar        *file;
 110 	const gchar        *name;
 111 	gchar              *abs_filename;
 112 	gint                cache_size;
 113 	gint                page_size;
 114 	gboolean            attached;
 115 	gboolean            is_index;
 116 	guint64             mtime;
 117 } TrackerDBDefinition;
 118 
 119 static TrackerDBDefinition dbs[] = {
 120 	{ TRACKER_DB_UNKNOWN,
 121 	  TRACKER_DB_LOCATION_USER_DATA_DIR,
 122 	  NULL,
 123 	  NULL,
 124 	  NULL,
 125 	  NULL,
 126 	  32,
 127 	  TRACKER_DB_PAGE_SIZE_DONT_SET,
 128 	  FALSE,
 129 	  FALSE,
 130 	  0 },
 131 	{ TRACKER_DB_METADATA,
 132 	  TRACKER_DB_LOCATION_DATA_DIR,
 133 	  NULL,
 134 	  "meta.db",
 135 	  "meta",
 136 	  NULL,
 137 	  TRACKER_DB_CACHE_SIZE_DEFAULT,
 138 	  8192,
 139 	  FALSE,
 140 	  FALSE,
 141 	  0 },
 142 };
 143 
 144 static gboolean            db_exec_no_reply                        (TrackerDBInterface   *iface,
 145                                                                     const gchar          *query,
 146                                                                     ...);
 147 static TrackerDBInterface *db_interface_create                      (TrackerDB            db,
 148                                                                      GError             **error);
 149 static TrackerDBInterface *tracker_db_manager_get_db_interfaces     (GError             **error,
 150                                                                      gint                 num, ...);
 151 static TrackerDBInterface *tracker_db_manager_get_db_interfaces_ro  (GError             **error,
 152                                                                      gint                 num, ...);
 153 static void                db_remove_locale_file                    (void);
 154 
 155 static gboolean              initialized;
 156 static gboolean              locations_initialized;
 157 static gchar                *data_dir = NULL;
 158 static gchar                *user_data_dir = NULL;
 159 static gchar                *in_use_filename = NULL;
 160 static gpointer              db_type_enum_class_pointer;
 161 static TrackerDBManagerFlags old_flags = 0;
 162 static guint                 s_cache_size;
 163 static guint                 u_cache_size;
 164 
 165 #if GLIB_CHECK_VERSION (2,31,0)
 166 static GPrivate              interface_data_key = G_PRIVATE_INIT ((GDestroyNotify)g_object_unref);
 167 #else
 168 static GStaticPrivate        interface_data_key = G_STATIC_PRIVATE_INIT;
 169 #endif
 170 
 171 /* mutex used by singleton connection in libtracker-direct, not used by tracker-store */
 172 static GMutex                global_mutex;
 173 
 174 static TrackerDBInterface   *global_iface;
 175 
 176 static const gchar *
 177 location_to_directory (TrackerDBLocation location)
 178 {
 179 	switch (location) {
 180 	case TRACKER_DB_LOCATION_DATA_DIR:
 181 		return data_dir;
 182 	case TRACKER_DB_LOCATION_USER_DATA_DIR:
 183 		return user_data_dir;
 184 	default:
 185 		return NULL;
 186 	};
 187 }
 188 
 189 static gboolean
 190 db_exec_no_reply (TrackerDBInterface *iface,
 191                   const gchar        *query,
 192                   ...)
 193 {
 194 	va_list                     args;
 195 
 196 	va_start (args, query);
 197 	tracker_db_interface_execute_vquery (iface, NULL, query, args);
 198 	va_end (args);
 199 
 200 	return TRUE;
 201 }
 202 
 203 TrackerDBManagerFlags
 204 tracker_db_manager_get_flags (guint *select_cache_size, guint *update_cache_size)
 205 {
 206 	if (select_cache_size)
 207 		*select_cache_size = s_cache_size;
 208 
 209 	if (update_cache_size)
 210 		*update_cache_size = u_cache_size;
 211 
 212 	return old_flags;
 213 }
 214 
 215 static void
 216 db_set_params (TrackerDBInterface   *iface,
 217                gint                  cache_size,
 218                gint                  page_size,
 219                GError              **error)
 220 {
 221 	gchar *queries = NULL;
 222 	const gchar *pragmas_file;
 223 
 224 	pragmas_file = g_getenv ("TRACKER_PRAGMAS_FILE");
 225 
 226 	if (pragmas_file && g_file_get_contents (pragmas_file, &queries, NULL, NULL)) {
 227 		gchar *query = strtok (queries, "\n");
 228 		g_debug ("PRAGMA's from file: %s", pragmas_file);
 229 		while (query) {
 230 			g_debug ("  INIT query: %s", query);
 231 			tracker_db_interface_execute_query (iface, NULL, "%s", query);
 232 			query = strtok (NULL, "\n");
 233 		}
 234 		g_free (queries);
 235 	} else {
 236 		GError *internal_error = NULL;
 237 		TrackerDBStatement *stmt;
 238 
 239 #ifdef DISABLE_JOURNAL
 240 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA synchronous = NORMAL;");
 241 #else
 242 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA synchronous = OFF;");
 243 #endif /* DISABLE_JOURNAL */
 244 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = FILE;");
 245 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA encoding = \"UTF-8\"");
 246 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA auto_vacuum = 0;");
 247 
 248 		stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
 249 		                                              &internal_error,
 250 		                                              "PRAGMA journal_mode = WAL;");
 251 
 252 		if (internal_error) {
 253 			g_message ("Can't set journal mode to WAL: '%s'",
 254 			           internal_error->message);
 255 			g_propagate_error (error, internal_error);
 256 		} else {
 257 			TrackerDBCursor *cursor;
 258 
 259 			cursor = tracker_db_statement_start_cursor (stmt, NULL);
 260 			if (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
 261 				if (g_ascii_strcasecmp (tracker_db_cursor_get_string (cursor, 0, NULL), "WAL") != 0) {
 262 					g_set_error (error,
 263 					             TRACKER_DB_INTERFACE_ERROR,
 264 					             TRACKER_DB_OPEN_ERROR,
 265 					             "Can't set journal mode to WAL");
 266 				}
 267 			}
 268 			g_object_unref (cursor);
 269 		}
 270 
 271 		if (stmt) {
 272 			g_object_unref (stmt);
 273 		}
 274 
 275 		/* disable autocheckpoint */
 276 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA wal_autocheckpoint = 0");
 277 
 278 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA journal_size_limit = 10240000");
 279 
 280 		if (page_size != TRACKER_DB_PAGE_SIZE_DONT_SET) {
 281 			g_message ("  Setting page size to %d", page_size);
 282 			tracker_db_interface_execute_query (iface, NULL, "PRAGMA page_size = %d", page_size);
 283 		}
 284 
 285 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d", cache_size);
 286 		g_message ("  Setting cache size to %d", cache_size);
 287 	}
 288 }
 289 
 290 
 291 static const gchar *
 292 db_type_to_string (TrackerDB db)
 293 {
 294 	GType       type;
 295 	GEnumClass *enum_class;
 296 	GEnumValue *enum_value;
 297 
 298 	type = tracker_db_get_type ();
 299 	enum_class = G_ENUM_CLASS (g_type_class_peek (type));
 300 	enum_value = g_enum_get_value (enum_class, db);
 301 
 302 	if (!enum_value) {
 303 		return "unknown";
 304 	}
 305 
 306 	return enum_value->value_nick;
 307 }
 308 
 309 static TrackerDBInterface *
 310 db_interface_get (TrackerDB   type,
 311                   gboolean   *create,
 312                   GError    **error)
 313 {
 314 	TrackerDBInterface *iface;
 315 	const gchar *path;
 316 	GError *internal_error = NULL;
 317 
 318 	path = dbs[type].abs_filename;
 319 
 320 	if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
 321 		*create = TRUE;
 322 	} else {
 323 		*create = FALSE;
 324 	}
 325 
 326 	g_message ("%s database... '%s' (%s)",
 327 	           *create ? "Creating" : "Loading",
 328 	           path,
 329 	           db_type_to_string (type));
 330 
 331 	iface = tracker_db_interface_sqlite_new (path,
 332 	                                         &internal_error);
 333 
 334 	if (internal_error) {
 335 		g_propagate_error (error, internal_error);
 336 		return NULL;
 337 	}
 338 
 339 	db_set_params (iface,
 340 	               dbs[type].cache_size,
 341 	               dbs[type].page_size,
 342 	               &internal_error);
 343 
 344 	if (internal_error) {
 345 		g_propagate_error (error, internal_error);
 346 		return NULL;
 347 	}
 348 
 349 	return iface;
 350 }
 351 
 352 static TrackerDBInterface *
 353 db_interface_get_metadata (GError **error)
 354 {
 355 	TrackerDBInterface *iface;
 356 	gboolean create;
 357 	GError *internal_error = NULL;
 358 
 359 	iface = db_interface_get (TRACKER_DB_METADATA, &create, &internal_error);
 360 
 361 	if (internal_error) {
 362 		g_propagate_error (error, internal_error);
 363 		return NULL;
 364 	}
 365 
 366 	return iface;
 367 }
 368 
 369 static TrackerDBInterface *
 370 db_interface_create (TrackerDB db,
 371                      GError **error)
 372 {
 373 	TrackerDBInterface *iface;
 374 	GError *internal_error = NULL;
 375 
 376 	switch (db) {
 377 	case TRACKER_DB_UNKNOWN:
 378 		return NULL;
 379 
 380 	case TRACKER_DB_METADATA:
 381 		iface = db_interface_get_metadata (&internal_error);
 382 		if (internal_error) {
 383 			g_propagate_error (error, internal_error);
 384 			return NULL;
 385 		}
 386 		return iface;
 387 
 388 	default:
 389 		g_critical ("This TrackerDB type:%d->'%s' has no interface set up yet!!",
 390 		            db,
 391 		            db_type_to_string (db));
 392 		return NULL;
 393 	}
 394 }
 395 
 396 static void
 397 db_manager_remove_journal (void)
 398 {
 399 #ifndef DISABLE_JOURNAL
 400 	gchar *path;
 401 	gchar *directory, *rotate_to = NULL;
 402 	gsize chunk_size;
 403 	gboolean do_rotate = FALSE;
 404 	const gchar *dirs[3] = { NULL, NULL, NULL };
 405 	guint i;
 406 	GError *error = NULL;
 407 
 408 	/* We duplicate the path here because later we shutdown the
 409 	 * journal which frees this data. We want to survive that.
 410 	 */
 411 	path = g_strdup (tracker_db_journal_get_filename ());
 412 	if (!path) {
 413 		return;
 414 	}
 415 
 416 	g_message ("  Removing journal:'%s'", path);
 417 
 418 	directory = g_path_get_dirname (path);
 419 
 420 	tracker_db_journal_get_rotating (&do_rotate, &chunk_size, &rotate_to);
 421 	tracker_db_journal_shutdown (&error);
 422 
 423 	if (error) {
 424 		/* TODO: propagate error */
 425 		g_message ("Ignored error while shutting down journal during remove: %s",
 426 		           error->message ? error->message : "No error given");
 427 		g_error_free (error);
 428 	}
 429 
 430 	dirs[0] = directory;
 431 	dirs[1] = do_rotate ? rotate_to : NULL;
 432 
 433 	for (i = 0; dirs[i] != NULL; i++) {
 434 		GDir *journal_dir;
 435 		const gchar *f;
 436 
 437 		journal_dir = g_dir_open (dirs[i], 0, NULL);
 438 		if (!journal_dir) {
 439 			continue;
 440 		}
 441 
 442 		/* Remove rotated chunks */
 443 		while ((f = g_dir_read_name (journal_dir)) != NULL) {
 444 			gchar *fullpath;
 445 
 446 			if (!g_str_has_prefix (f, TRACKER_DB_JOURNAL_FILENAME ".")) {
 447 				continue;
 448 			}
 449 
 450 			fullpath = g_build_filename (dirs[i], f, NULL);
 451 			if (g_unlink (fullpath) == -1) {
 452 				g_message ("%s", g_strerror (errno));
 453 			}
 454 			g_free (fullpath);
 455 		}
 456 
 457 		g_dir_close (journal_dir);
 458 	}
 459 
 460 	g_free (rotate_to);
 461 	g_free (directory);
 462 
 463 	/* Remove active journal */
 464 	if (g_unlink (path) == -1) {
 465 		g_message ("%s", g_strerror (errno));
 466 	}
 467 	g_free (path);
 468 #endif /* DISABLE_JOURNAL */
 469 }
 470 
 471 static void
 472 db_manager_remove_all (gboolean rm_journal)
 473 {
 474 	guint i;
 475 
 476 	g_message ("Removing all database/storage files");
 477 
 478 	/* Remove stamp files */
 479 	tracker_db_manager_set_first_index_done (FALSE);
 480 	tracker_db_manager_set_last_crawl_done (FALSE);
 481 	tracker_db_manager_set_need_mtime_check (TRUE);
 482 
 483 	/* NOTE: We don't have to be initialized for this so we
 484 	 * calculate the absolute directories here.
 485 	 */
 486 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
 487 		gchar *filename;
 488 
 489 		g_message ("  Removing database:'%s'", dbs[i].abs_filename);
 490 		g_unlink (dbs[i].abs_filename);
 491 
 492 		/* also delete shm and wal helper files */
 493 		filename = g_strdup_printf ("%s-shm", dbs[i].abs_filename);
 494 		g_unlink (filename);
 495 		g_free (filename);
 496 
 497 		filename = g_strdup_printf ("%s-wal", dbs[i].abs_filename);
 498 		g_unlink (filename);
 499 		g_free (filename);
 500 	}
 501 
 502 	if (rm_journal) {
 503 		db_manager_remove_journal ();
 504 
 505 		/* If also the journal is gone, we can also remove db-version.txt, it
 506 		 * would have no more relevance whatsoever. */
 507 		tracker_db_manager_remove_version_file ();
 508 	}
 509 
 510 	/* Remove locale file also */
 511 	db_remove_locale_file ();
 512 }
 513 
 514 static TrackerDBVersion
 515 db_get_version (void)
 516 {
 517 	TrackerDBVersion  version;
 518 	gchar            *filename;
 519 
 520 	filename = g_build_filename (data_dir, TRACKER_DB_VERSION_FILE, NULL);
 521 
 522 	if (G_LIKELY (g_file_test (filename, G_FILE_TEST_EXISTS))) {
 523 		gchar *contents;
 524 
 525 		/* Check version is correct */
 526 		if (G_LIKELY (g_file_get_contents (filename, &contents, NULL, NULL))) {
 527 			if (contents && strlen (contents) <= 2) {
 528 				version = atoi (contents);
 529 			} else {
 530 				g_message ("  Version file content size is either 0 or bigger than expected");
 531 
 532 				version = TRACKER_DB_VERSION_UNKNOWN;
 533 			}
 534 
 535 			g_free (contents);
 536 		} else {
 537 			g_message ("  Could not get content of file '%s'", filename);
 538 
 539 			version = TRACKER_DB_VERSION_UNKNOWN;
 540 		}
 541 	} else {
 542 		g_message ("  Could not find database version file:'%s'", filename);
 543 		g_message ("  Current databases are either old or no databases are set up yet");
 544 
 545 		version = TRACKER_DB_VERSION_UNKNOWN;
 546 	}
 547 
 548 	g_free (filename);
 549 
 550 	return version;
 551 }
 552 
 553 void
 554 tracker_db_manager_create_version_file (void)
 555 {
 556 	GError *error = NULL;
 557 	gchar  *filename;
 558 	gchar  *str;
 559 
 560 	filename = g_build_filename (data_dir, TRACKER_DB_VERSION_FILE, NULL);
 561 	g_message ("  Creating version file '%s'", filename);
 562 
 563 	str = g_strdup_printf ("%d", TRACKER_DB_VERSION_NOW);
 564 
 565 	if (!g_file_set_contents (filename, str, -1, &error)) {
 566 		g_message ("  Could not set file contents, %s",
 567 		           error ? error->message : "no error given");
 568 		g_clear_error (&error);
 569 	}
 570 
 571 	g_free (str);
 572 	g_free (filename);
 573 }
 574 
 575 void
 576 tracker_db_manager_remove_version_file (void)
 577 {
 578 	gchar *filename;
 579 
 580 	filename = g_build_filename (data_dir, TRACKER_DB_VERSION_FILE, NULL);
 581 	g_message ("  Removing db-version file:'%s'", filename);
 582 	g_unlink (filename);
 583 	g_free (filename);
 584 }
 585 
 586 static void
 587 db_remove_locale_file (void)
 588 {
 589 	gchar *filename;
 590 
 591 	filename = g_build_filename (data_dir, TRACKER_DB_LOCALE_FILE, NULL);
 592 	g_message ("  Removing db-locale file:'%s'", filename);
 593 	g_unlink (filename);
 594 	g_free (filename);
 595 }
 596 
 597 static gchar *
 598 db_get_locale (void)
 599 {
 600 	gchar *locale = NULL;
 601 	gchar *filename;
 602 
 603 	filename = g_build_filename (data_dir, TRACKER_DB_LOCALE_FILE, NULL);
 604 
 605 	if (G_LIKELY (g_file_test (filename, G_FILE_TEST_EXISTS))) {
 606 		gchar *contents;
 607 
 608 		/* Check locale is correct */
 609 		if (G_LIKELY (g_file_get_contents (filename, &contents, NULL, NULL))) {
 610 			if (contents && strlen (contents) == 0) {
 611 				g_critical ("  Empty locale file found at '%s'", filename);
 612 				g_free (contents);
 613 			} else {
 614 				/* Re-use contents */
 615 				locale = contents;
 616 			}
 617 		} else {
 618 			g_critical ("  Could not get content of file '%s'", filename);
 619 		}
 620 	} else {
 621 		/* expected when restoring from backup, always recreate indices */
 622 		g_message ("  Could not find database locale file:'%s'", filename);
 623 		locale = g_strdup ("unknown");
 624 	}
 625 
 626 	g_free (filename);
 627 
 628 	return locale;
 629 }
 630 
 631 static void
 632 db_set_locale (const gchar *locale)
 633 {
 634 	GError *error = NULL;
 635 	gchar  *filename;
 636 	gchar  *str;
 637 
 638 	filename = g_build_filename (data_dir, TRACKER_DB_LOCALE_FILE, NULL);
 639 	g_message ("  Creating locale file '%s'", filename);
 640 
 641 	str = g_strdup_printf ("%s", locale ? locale : "");
 642 
 643 	if (!g_file_set_contents (filename, str, -1, &error)) {
 644 		g_message ("  Could not set file contents, %s",
 645 		           error ? error->message : "no error given");
 646 		g_clear_error (&error);
 647 	}
 648 
 649 	g_free (str);
 650 	g_free (filename);
 651 }
 652 
 653 gboolean
 654 tracker_db_manager_locale_changed (void)
 655 {
 656 	gchar *db_locale;
 657 	gchar *current_locale;
 658 	gboolean changed;
 659 
 660 	/* Get current collation locale */
 661 	current_locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
 662 
 663 	/* Get db locale */
 664 	db_locale = db_get_locale ();
 665 
 666 	/* If they are different, recreate indexes. Note that having
 667 	 * both to NULL is actually valid, they would default to
 668 	 * the unicode collation without locale-specific stuff. */
 669 	if (g_strcmp0 (db_locale, current_locale) != 0) {
 670 		g_message ("Locale change detected from '%s' to '%s'...",
 671 		           db_locale, current_locale);
 672 		changed = TRUE;
 673 	} else {
 674 		g_message ("Current and DB locales match: '%s'", db_locale);
 675 		changed = FALSE;
 676 	}
 677 
 678 	g_free (db_locale);
 679 	g_free (current_locale);
 680 	return changed;
 681 }
 682 
 683 void
 684 tracker_db_manager_set_current_locale (void)
 685 {
 686 	gchar *current_locale;
 687 
 688 	/* Get current collation locale */
 689 	current_locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
 690 	g_message ("Changing db locale to: '%s'", current_locale);
 691 	db_set_locale (current_locale);
 692 	g_free (current_locale);
 693 }
 694 
 695 static void
 696 db_manager_analyze (TrackerDB           db,
 697                     TrackerDBInterface *iface)
 698 {
 699 	guint64             current_mtime;
 700 
 701 	current_mtime = tracker_file_get_mtime (dbs[db].abs_filename);
 702 
 703 	if (current_mtime > dbs[db].mtime) {
 704 		g_message ("  Analyzing DB:'%s'", dbs[db].name);
 705 		db_exec_no_reply (iface, "ANALYZE %s.Services", dbs[db].name);
 706 
 707 		/* Remember current mtime for future */
 708 		dbs[db].mtime = current_mtime;
 709 	} else {
 710 		g_message ("  Not updating DB:'%s', no changes since last optimize", dbs[db].name);
 711 	}
 712 }
 713 
 714 GType
 715 tracker_db_get_type (void)
 716 {
 717 	static GType etype = 0;
 718 
 719 	if (etype == 0) {
 720 		static const GEnumValue values[] = {
 721 			{ TRACKER_DB_METADATA,
 722 			  "TRACKER_DB_METADATA",
 723 			  "metadata" },
 724 		};
 725 
 726 		etype = g_enum_register_static ("TrackerDB", values);
 727 	}
 728 
 729 	return etype;
 730 }
 731 
 732 static void
 733 db_recreate_all (GError **error)
 734 {
 735 	guint i;
 736 	gchar *locale;
 737 	GError *internal_error = NULL;
 738 
 739 	/* We call an internal version of this function here
 740 	 * because at the time 'initialized' = FALSE and that
 741 	 * will cause errors and do nothing.
 742 	 */
 743 	g_message ("Cleaning up database files for reindex");
 744 
 745 	db_manager_remove_all (FALSE);
 746 
 747 	/* Now create the databases and close them */
 748 	g_message ("Creating database files, this may take a few moments...");
 749 
 750 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
 751 		dbs[i].iface = db_interface_create (i, &internal_error);
 752 		if (internal_error) {
 753 			guint y;
 754 
 755 			for (y = 1; y < i; y++) {
 756 				g_object_unref (dbs[y].iface);
 757 				dbs[y].iface = NULL;
 758 			}
 759 
 760 			g_propagate_error (error, internal_error);
 761 
 762 			return;
 763 		}
 764 	}
 765 
 766 	/* We don't close the dbs in the same loop as before
 767 	 * becase some databases need other databases
 768 	 * attached to be created correctly.
 769 	 */
 770 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
 771 		g_object_unref (dbs[i].iface);
 772 		dbs[i].iface = NULL;
 773 	}
 774 
 775 	locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
 776 	/* Initialize locale file */
 777 	db_set_locale (locale);
 778 	g_free (locale);
 779 }
 780 
 781 void
 782 tracker_db_manager_init_locations (void)
 783 {
 784 	const gchar *dir;
 785 	guint i;
 786 
 787 	user_data_dir = g_build_filename (g_get_user_data_dir (),
 788 	                                  "tracker",
 789 	                                  "data",
 790 	                                  NULL);
 791 
 792 	/* For DISABLE_JOURNAL case we should use g_get_user_data_dir here. For now
 793 	 * keeping this as-is */
 794 
 795 	data_dir = g_build_filename (g_get_user_cache_dir (),
 796 	                             "tracker",
 797 	                             NULL);
 798 
 799 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
 800 		dir = location_to_directory (dbs[i].location);
 801 		dbs[i].abs_filename = g_build_filename (dir, dbs[i].file, NULL);
 802 	}
 803 
 804 	locations_initialized = TRUE;
 805 }
 806 
 807 static void
 808 perform_recreate (gboolean *first_time, GError **error)
 809 {
 810 	GError *internal_error = NULL;
 811 	guint i;
 812 
 813 	if (first_time) {
 814 		*first_time = TRUE;
 815 	}
 816 
 817 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
 818 		if (dbs[i].iface) {
 819 			g_object_unref (dbs[i].iface);
 820 			dbs[i].iface = NULL;
 821 		}
 822 	}
 823 
 824 	if (!tracker_file_system_has_enough_space (data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, TRUE)) {
 825 		g_set_error (error,
 826 		             TRACKER_DB_INTERFACE_ERROR,
 827 		             TRACKER_DB_OPEN_ERROR,
 828 		             "Filesystem has not enough space");
 829 		return;
 830 	}
 831 
 832 	db_recreate_all (&internal_error);
 833 
 834 	if (internal_error) {
 835 		g_propagate_error (error, internal_error);
 836 	}
 837 }
 838 
 839 gboolean
 840 tracker_db_manager_init (TrackerDBManagerFlags   flags,
 841                          gboolean               *first_time,
 842                          gboolean                restoring_backup,
 843                          gboolean                shared_cache,
 844                          guint                   select_cache_size,
 845                          guint                   update_cache_size,
 846                          TrackerBusyCallback     busy_callback,
 847                          gpointer                busy_user_data,
 848                          const gchar            *busy_operation,
 849                          GError                **error)
 850 {
 851 	GType etype;
 852 	TrackerDBVersion version;
 853 	const gchar *dir;
 854 	gboolean need_reindex;
 855 	guint i;
 856 	int in_use_file;
 857 	gboolean loaded = FALSE;
 858 	TrackerDBInterface *resources_iface;
 859 	GError *internal_error = NULL;
 860 
 861 	/* First set defaults for return values */
 862 	if (first_time) {
 863 		*first_time = FALSE;
 864 	}
 865 
 866 	if (initialized) {
 867 		return TRUE;
 868 	}
 869 
 870 	need_reindex = FALSE;
 871 
 872 	/* Since we don't reference this enum anywhere, we do
 873 	 * it here to make sure it exists when we call
 874 	 * g_type_class_peek(). This wouldn't be necessary if
 875 	 * it was a param in a GObject for example.
 876 	 *
 877 	 * This does mean that we are leaking by 1 reference
 878 	 * here and should clean it up, but it doesn't grow so
 879 	 * this is acceptable.
 880 	 */
 881 	etype = tracker_db_get_type ();
 882 	db_type_enum_class_pointer = g_type_class_ref (etype);
 883 
 884 	/* Set up locations */
 885 	g_message ("Setting database locations");
 886 
 887 	old_flags = flags;
 888 
 889 	g_free (user_data_dir);
 890 	user_data_dir = g_build_filename (g_get_user_data_dir (),
 891 	                                  "tracker",
 892 	                                  "data",
 893 	                                  NULL);
 894 
 895 	g_free (data_dir);
 896 	data_dir = g_build_filename (g_get_user_cache_dir (),
 897 	                             "tracker",
 898 	                             NULL);
 899 
 900 	g_free (in_use_filename);
 901 	in_use_filename = g_build_filename (g_get_user_data_dir (),
 902 	                                    "tracker",
 903 	                                    "data",
 904 	                                    IN_USE_FILENAME,
 905 	                                    NULL);
 906 
 907 	/* Don't do need_reindex checks for readonly (direct-access) */
 908 	if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
 909 
 910 		/* Make sure the directories exist */
 911 		g_message ("Checking database directories exist");
 912 
 913 		g_mkdir_with_parents (data_dir, 00755);
 914 		g_mkdir_with_parents (user_data_dir, 00755);
 915 
 916 		g_message ("Checking database version");
 917 
 918 		version = db_get_version ();
 919 
 920 		if (version < TRACKER_DB_VERSION_NOW) {
 921 			g_message ("  A reindex will be forced");
 922 			need_reindex = TRUE;
 923 		}
 924 
 925 		if (need_reindex) {
 926 			tracker_db_manager_create_version_file ();
 927 			tracker_db_manager_set_need_mtime_check (TRUE);
 928 		}
 929 	}
 930 
 931 	g_message ("Checking database files exist");
 932 
 933 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
 934 		/* Fill absolute path for the database */
 935 
 936 		dir = location_to_directory (dbs[i].location);
 937 		g_free (dbs[i].abs_filename);
 938 		dbs[i].abs_filename = g_build_filename (dir, dbs[i].file, NULL);
 939 
 940 		/* Check we have each database in place, if one is
 941 		 * missing, we reindex.
 942 		 */
 943 
 944 		if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
 945 			/* No need to check for other files not existing (for
 946 			 * reindex) if one is already missing.
 947 			 */
 948 			if (need_reindex) {
 949 				continue;
 950 			}
 951 		}
 952 
 953 		if (!g_file_test (dbs[i].abs_filename, G_FILE_TEST_EXISTS)) {
 954 			if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
 955 				g_message ("Could not find database file:'%s'", dbs[i].abs_filename);
 956 				g_message ("One or more database files are missing, a reindex will be forced");
 957 				need_reindex = TRUE;
 958 			} else {
 959 				guint y;
 960 
 961 				g_set_error (error,
 962 				             TRACKER_DB_INTERFACE_ERROR,
 963 				             TRACKER_DB_OPEN_ERROR,
 964 				             "Could not find database file:'%s'. One or more database files are missing", dbs[i].abs_filename);
 965 
 966 				for (y = 1; y <= i; y++) {
 967 					g_free (dbs[y].abs_filename);
 968 					dbs[y].abs_filename = NULL;
 969 				}
 970 
 971 				return FALSE;
 972 			}
 973 		}
 974 	}
 975 
 976 	locations_initialized = TRUE;
 977 
 978 	/* Don't do remove-dbs for readonly (direct-access) */
 979 	if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
 980 
 981 		/* If we are just initializing to remove the databases,
 982 		 * return here.
 983 		 */
 984 		if ((flags & TRACKER_DB_MANAGER_REMOVE_ALL) != 0) {
 985 			initialized = TRUE;
 986 			return TRUE;
 987 		}
 988 	}
 989 
 990 	/* Set general database options */
 991 	if (shared_cache) {
 992 		g_message ("Enabling database shared cache");
 993 		tracker_db_interface_sqlite_enable_shared_cache ();
 994 	}
 995 
 996 	/* Should we reindex? If so, just remove all databases files,
 997 	 * NOT the paths, note, that these paths are also used for
 998 	 * other things like the nfs lock file.
 999 	 */
1000 	if (flags & TRACKER_DB_MANAGER_FORCE_REINDEX || need_reindex) {
1001 
1002 		if (flags & TRACKER_DB_MANAGER_READONLY) {
1003 			/* no reindexing supported in read-only mode (direct access) */
1004 
1005 			g_set_error (error,
1006 			             TRACKER_DB_INTERFACE_ERROR,
1007 			             TRACKER_DB_OPEN_ERROR,
1008 			             "No reindexing supported in read-only mode (direct access)");
1009 
1010 			return FALSE;
1011 		}
1012 
1013 		/* Clear the first-index stamp file */
1014 		tracker_db_manager_set_first_index_done (FALSE);
1015 
1016 		perform_recreate (first_time, &internal_error);
1017 
1018 		if (internal_error) {
1019 			g_propagate_error (error, internal_error);
1020 			return FALSE;
1021 		}
1022 
1023 		/* Load databases */
1024 		g_message ("Loading databases files...");
1025 
1026 	} else if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
1027 		/* do not do shutdown check for read-only mode (direct access) */
1028 		gboolean must_recreate = FALSE;
1029 #ifndef DISABLE_JOURNAL
1030 		gchar *journal_filename;
1031 #endif /* DISABLE_JOURNAL */
1032 
1033 		/* Load databases */
1034 		g_message ("Loading databases files...");
1035 
1036 #ifndef DISABLE_JOURNAL
1037 		journal_filename = g_build_filename (g_get_user_data_dir (),
1038 		                                     "tracker",
1039 		                                     "data",
1040 		                                     TRACKER_DB_JOURNAL_FILENAME,
1041 		                                     NULL);
1042 
1043 		must_recreate = !tracker_db_journal_reader_verify_last (journal_filename,
1044 		                                                        NULL);
1045 
1046 		g_free (journal_filename);
1047 #endif /* DISABLE_JOURNAL */
1048 
1049 		if (!must_recreate && g_file_test (in_use_filename, G_FILE_TEST_EXISTS)) {
1050 			gsize size = 0;
1051 
1052 			g_message ("Didn't shut down cleanly last time, doing integrity checks");
1053 
1054 			for (i = 1; i < G_N_ELEMENTS (dbs) && !must_recreate; i++) {
1055 				struct stat st;
1056 				TrackerDBStatement *stmt;
1057 #ifndef DISABLE_JOURNAL
1058 				gchar *busy_status;
1059 #endif /* DISABLE_JOURNAL */
1060 
1061 				if (g_stat (dbs[i].abs_filename, &st) == 0) {
1062 					size = st.st_size;
1063 				}
1064 
1065 				/* Size is 1 when using echo > file.db, none of our databases
1066 				 * are only one byte in size even initually. */
1067 
1068 				if (size <= 1) {
1069 					if (!restoring_backup) {
1070 						must_recreate = TRUE;
1071 					} else {
1072 						g_set_error (&internal_error,
1073 						             TRACKER_DB_INTERFACE_ERROR,
1074 						             TRACKER_DB_OPEN_ERROR,
1075 						             "Corrupt db file");
1076 					}
1077 					continue;
1078 				}
1079 
1080 				dbs[i].iface = db_interface_create (i, &internal_error);
1081 
1082 				if (internal_error) {
1083 					/* If this already doesn't succeed, then surely the file is
1084 					 * corrupt. No need to check for integrity anymore. */
1085 					if (!restoring_backup) {
1086 						g_clear_error (&internal_error);
1087 						must_recreate = TRUE;
1088 					}
1089 					continue;
1090 				}
1091 
1092 				dbs[i].mtime = tracker_file_get_mtime (dbs[i].abs_filename);
1093 
1094 				loaded = TRUE;
1095 
1096 #ifndef DISABLE_JOURNAL
1097 				/* Report OPERATION - STATUS */
1098 				busy_status = g_strdup_printf ("%s - %s",
1099 				                               busy_operation,
1100 				                               "Integrity checking");
1101 				tracker_db_interface_set_busy_handler (dbs[i].iface,
1102 				                                       busy_callback,
1103 				                                       busy_status,
1104 				                                       busy_user_data);
1105 				g_free (busy_status);
1106 
1107 				stmt = tracker_db_interface_create_statement (dbs[i].iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
1108 				                                              &internal_error,
1109 				                                              "PRAGMA integrity_check(1)");
1110 
1111 				if (internal_error != NULL) {
1112 					if (internal_error->domain == TRACKER_DB_INTERFACE_ERROR &&
1113 					    internal_error->code == TRACKER_DB_QUERY_ERROR) {
1114 						must_recreate = TRUE;
1115 					} else {
1116 						g_critical ("%s", internal_error->message);
1117 					}
1118 					g_error_free (internal_error);
1119 					internal_error = NULL;
1120 				} else {
1121 					TrackerDBCursor *cursor = NULL;
1122 
1123 					if (stmt) {
1124 						cursor = tracker_db_statement_start_cursor (stmt, NULL);
1125 						g_object_unref (stmt);
1126 					} else {
1127 						g_critical ("Can't create stmt for integrity_check, no error given");
1128 					}
1129 
1130 					if (cursor) {
1131 						if (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
1132 							if (g_strcmp0 (tracker_db_cursor_get_string (cursor, 0, NULL), "ok") != 0) {
1133 								must_recreate = TRUE;
1134 							}
1135 						}
1136 						g_object_unref (cursor);
1137 					}
1138 				}
1139 #endif /* DISABLE_JOURNAL */
1140 
1141 				/* ensure that database has been initialized by an earlier tracker-store start
1142 				   by checking whether Resource table exists */
1143 				stmt = tracker_db_interface_create_statement (dbs[i].iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
1144 				                                              &internal_error,
1145 				                                              "SELECT 1 FROM Resource");
1146 				if (internal_error != NULL) {
1147 					if (!restoring_backup) {
1148 						must_recreate = TRUE;
1149 						g_error_free (internal_error);
1150 						internal_error = NULL;
1151 					} else {
1152 						continue;
1153 					}
1154 				} else {
1155 					g_object_unref (stmt);
1156 				}
1157 
1158 				tracker_db_interface_set_busy_handler (dbs[i].iface, NULL, NULL, NULL);
1159 			}
1160 		}
1161 
1162 		if (must_recreate) {
1163 			g_message ("Database severely damaged. We will recreate it"
1164 #ifndef DISABLE_JOURNAL
1165 			           " and replay the journal if available.");
1166 #else
1167 			           ".");
1168 #endif /* DISABLE_JOURNAL */
1169 
1170 			perform_recreate (first_time, &internal_error);
1171 			if (internal_error) {
1172 				g_propagate_error (error, internal_error);
1173 				return FALSE;
1174 			}
1175 			loaded = FALSE;
1176 		} else {
1177 			if (internal_error) {
1178 				g_propagate_error (error, internal_error);
1179 				return FALSE;
1180 			}
1181 		}
1182 	}
1183 
1184 	if (!loaded) {
1185 		for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
1186 			dbs[i].mtime = tracker_file_get_mtime (dbs[i].abs_filename);
1187 		}
1188 	}
1189 
1190 	if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
1191 		/* do not create in-use file for read-only mode (direct access) */
1192 		in_use_file = g_open (in_use_filename,
1193 			              O_WRONLY | O_APPEND | O_CREAT | O_SYNC,
1194 			              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
1195 
1196 		if (in_use_file >= 0) {
1197 		        fsync (in_use_file);
1198 		        close (in_use_file);
1199 		}
1200 	}
1201 
1202 	initialized = TRUE;
1203 
1204 	if (flags & TRACKER_DB_MANAGER_READONLY) {
1205 		resources_iface = tracker_db_manager_get_db_interfaces_ro (&internal_error, 1,
1206 		                                                           TRACKER_DB_METADATA);
1207 		/* libtracker-direct does not use per-thread interfaces */
1208 		global_iface = resources_iface;
1209 	} else {
1210 		resources_iface = tracker_db_manager_get_db_interfaces (&internal_error, 1,
1211 		                                                        TRACKER_DB_METADATA);
1212 	}
1213 
1214 	if (internal_error) {
1215 		if ((!restoring_backup) && (flags & TRACKER_DB_MANAGER_READONLY) == 0) {
1216 			GError *new_error = NULL;
1217 
1218 			perform_recreate (first_time, &new_error);
1219 			if (!new_error) {
1220 				resources_iface = tracker_db_manager_get_db_interfaces (&internal_error, 1,
1221 				                                                        TRACKER_DB_METADATA);
1222 			} else {
1223 				/* Most serious error is the recreate one here */
1224 				g_clear_error (&internal_error);
1225 				g_propagate_error (error, new_error);
1226 				initialized = FALSE;
1227 				return FALSE;
1228 			}
1229 		} else {
1230 			g_propagate_error (error, internal_error);
1231 			initialized = FALSE;
1232 			return FALSE;
1233 		}
1234 	}
1235 
1236 	tracker_db_interface_set_max_stmt_cache_size (resources_iface,
1237 	                                              TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT,
1238 	                                              select_cache_size);
1239 
1240 	tracker_db_interface_set_max_stmt_cache_size (resources_iface,
1241 	                                              TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
1242 	                                              update_cache_size);
1243 
1244 	s_cache_size = select_cache_size;
1245 	u_cache_size = update_cache_size;
1246 
1247 	if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
1248 #if GLIB_CHECK_VERSION (2,31,0)
1249 		g_private_replace (&interface_data_key, resources_iface);
1250 #else
1251 		g_static_private_set (&interface_data_key, resources_iface, (GDestroyNotify) g_object_unref);
1252 #endif
1253 	}
1254 
1255 	return TRUE;
1256 }
1257 
1258 void
1259 tracker_db_manager_shutdown (void)
1260 {
1261 	guint i;
1262 
1263 	if (!initialized) {
1264 		return;
1265 	}
1266 
1267 	for (i = 1; i < G_N_ELEMENTS (dbs); i++) {
1268 		if (dbs[i].abs_filename) {
1269 			g_free (dbs[i].abs_filename);
1270 			dbs[i].abs_filename = NULL;
1271 
1272 			if (dbs[i].iface) {
1273 				g_object_unref (dbs[i].iface);
1274 				dbs[i].iface = NULL;
1275 			}
1276 		}
1277 	}
1278 
1279 	g_free (data_dir);
1280 	data_dir = NULL;
1281 	g_free (user_data_dir);
1282 	user_data_dir = NULL;
1283 
1284 	if (global_iface) {
1285 		/* libtracker-direct */
1286 		g_object_unref (global_iface);
1287 		global_iface = NULL;
1288 	}
1289 
1290 	/* shutdown db interface in all threads */
1291 #if GLIB_CHECK_VERSION (2,31,0)
1292 	g_private_replace (&interface_data_key, NULL);
1293 #else
1294 	g_static_private_free (&interface_data_key);
1295 #endif
1296 
1297 	/* Since we don't reference this enum anywhere, we do
1298 	 * it here to make sure it exists when we call
1299 	 * g_type_class_peek(). This wouldn't be necessary if
1300 	 * it was a param in a GObject for example.
1301 	 *
1302 	 * This does mean that we are leaking by 1 reference
1303 	 * here and should clean it up, but it doesn't grow so
1304 	 * this is acceptable.
1305 	 */
1306 	g_type_class_unref (db_type_enum_class_pointer);
1307 	db_type_enum_class_pointer = NULL;
1308 
1309 	initialized = FALSE;
1310 	locations_initialized = FALSE;
1311 
1312 	if ((tracker_db_manager_get_flags (NULL, NULL) & TRACKER_DB_MANAGER_READONLY) == 0) {
1313 		/* do not delete in-use file for read-only mode (direct access) */
1314 		g_unlink (in_use_filename);
1315 	}
1316 
1317 	g_free (in_use_filename);
1318 	in_use_filename = NULL;
1319 }
1320 
1321 void
1322 tracker_db_manager_remove_all (gboolean rm_journal)
1323 {
1324 	g_return_if_fail (initialized != FALSE);
1325 
1326 	db_manager_remove_all (rm_journal);
1327 }
1328 
1329 void
1330 tracker_db_manager_optimize (void)
1331 {
1332 	gboolean dbs_are_open = FALSE;
1333 	TrackerDBInterface *iface;
1334 
1335 	g_return_if_fail (initialized != FALSE);
1336 
1337 	g_message ("Optimizing database...");
1338 
1339 	g_message ("  Checking database is not in use");
1340 
1341 	iface = tracker_db_manager_get_db_interface ();
1342 
1343 	/* Check if any connections are open? */
1344 	if (G_OBJECT (iface)->ref_count > 1) {
1345 		g_message ("  database is still in use with %d references!",
1346 		           G_OBJECT (iface)->ref_count);
1347 
1348 		dbs_are_open = TRUE;
1349 	}
1350 
1351 	if (dbs_are_open) {
1352 		g_message ("  Not optimizing database, still in use with > 1 reference");
1353 		return;
1354 	}
1355 
1356 	/* Optimize the metadata database */
1357 	db_manager_analyze (TRACKER_DB_METADATA, iface);
1358 }
1359 
1360 const gchar *
1361 tracker_db_manager_get_file (TrackerDB db)
1362 {
1363 	g_return_val_if_fail (initialized != FALSE, NULL);
1364 
1365 	return dbs[db].abs_filename;
1366 }
1367 
1368 /**
1369  * tracker_db_manager_get_db_interfaces:
1370  * @num: amount of TrackerDB files wanted
1371  * @...: All the files that you want in the connection as TrackerDB items
1372  *
1373  * Request a database connection where the first requested file gets connected
1374  * to and the subsequent requsted files get attached to the connection.
1375  *
1376  * The caller must g_object_unref the result when finished using it.
1377  *
1378  * returns: (caller-owns): a database connection
1379  **/
1380 static TrackerDBInterface *
1381 tracker_db_manager_get_db_interfaces (GError **error,
1382                                       gint num, ...)
1383 {
1384 	gint n_args;
1385 	va_list args;
1386 	TrackerDBInterface *connection = NULL;
1387 	GError *internal_error = NULL;
1388 
1389 	g_return_val_if_fail (initialized != FALSE, NULL);
1390 
1391 	va_start (args, num);
1392 	for (n_args = 1; n_args <= num; n_args++) {
1393 		TrackerDB db = va_arg (args, TrackerDB);
1394 
1395 		if (!connection) {
1396 			connection = tracker_db_interface_sqlite_new (dbs[db].abs_filename,
1397 			                                              &internal_error);
1398 
1399 			if (internal_error) {
1400 				g_propagate_error (error, internal_error);
1401 				connection = NULL;
1402 				goto end_on_error;
1403 			}
1404 
1405 			db_set_params (connection,
1406 			               dbs[db].cache_size,
1407 			               dbs[db].page_size,
1408 			               &internal_error);
1409 
1410 			if (internal_error) {
1411 				g_propagate_error (error, internal_error);
1412 				connection = NULL;
1413 				goto end_on_error;
1414 			}
1415 
1416 		} else {
1417 			db_exec_no_reply (connection,
1418 			                  "ATTACH '%s' as '%s'",
1419 			                  dbs[db].abs_filename,
1420 			                  dbs[db].name);
1421 		}
1422 
1423 	}
1424 
1425 	end_on_error:
1426 
1427 	va_end (args);
1428 
1429 	return connection;
1430 }
1431 
1432 static TrackerDBInterface *
1433 tracker_db_manager_get_db_interfaces_ro (GError **error,
1434                                          gint num, ...)
1435 {
1436 	gint n_args;
1437 	va_list args;
1438 	TrackerDBInterface *connection = NULL;
1439 	GError *internal_error = NULL;
1440 
1441 	g_return_val_if_fail (initialized != FALSE, NULL);
1442 
1443 	va_start (args, num);
1444 	for (n_args = 1; n_args <= num; n_args++) {
1445 		TrackerDB db = va_arg (args, TrackerDB);
1446 
1447 		if (!connection) {
1448 			connection = tracker_db_interface_sqlite_new_ro (dbs[db].abs_filename,
1449 			                                                 &internal_error);
1450 
1451 			if (internal_error) {
1452 				g_propagate_error (error, internal_error);
1453 				connection = NULL;
1454 				goto end_on_error;
1455 			}
1456 
1457 			db_set_params (connection,
1458 			               dbs[db].cache_size,
1459 			               dbs[db].page_size,
1460 			               &internal_error);
1461 
1462 			if (internal_error) {
1463 				g_propagate_error (error, internal_error);
1464 				connection = NULL;
1465 				goto end_on_error;
1466 			}
1467 
1468 		} else {
1469 			db_exec_no_reply (connection,
1470 			                  "ATTACH '%s' as '%s'",
1471 			                  dbs[db].abs_filename,
1472 			                  dbs[db].name);
1473 		}
1474 	}
1475 
1476 	end_on_error:
1477 
1478 	va_end (args);
1479 
1480 	return connection;
1481 }
1482 
1483 /**
1484  * tracker_db_manager_get_db_interface:
1485  *
1486  * Request a database connection to the database
1487  *
1488  * The caller must NOT g_object_unref the result
1489  *
1490  * returns: (callee-owns): a database connection
1491  **/
1492 TrackerDBInterface *
1493 tracker_db_manager_get_db_interface (void)
1494 {
1495 	GError *internal_error = NULL;
1496 	TrackerDBInterface *interface;
1497 
1498 	g_return_val_if_fail (initialized != FALSE, NULL);
1499 
1500 	if (global_iface) {
1501 		/* libtracker-direct */
1502 		return global_iface;
1503 	}
1504 
1505 #if GLIB_CHECK_VERSION (2,31,0)
1506 	interface = g_private_get (&interface_data_key);
1507 #else
1508 	interface = g_static_private_get (&interface_data_key);
1509 #endif
1510 
1511 	/* Ensure the interface is there */
1512 	if (!interface) {
1513 		interface = tracker_db_manager_get_db_interfaces (&internal_error, 1,
1514 		                                                  TRACKER_DB_METADATA);
1515 
1516 		if (internal_error) {
1517 			g_critical ("Error opening database: %s", internal_error->message);
1518 			g_error_free (internal_error);
1519 			return NULL;
1520 		}
1521 
1522 		tracker_data_manager_init_fts (interface, FALSE);
1523 
1524 		tracker_db_interface_set_max_stmt_cache_size (interface,
1525 		                                              TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT,
1526 		                                              s_cache_size);
1527 
1528 		tracker_db_interface_set_max_stmt_cache_size (interface,
1529 		                                              TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
1530 		                                              u_cache_size);
1531 
1532 #if GLIB_CHECK_VERSION (2,31,0)
1533 		g_private_set (&interface_data_key, interface);
1534 #else
1535 		g_static_private_set (&interface_data_key, interface, (GDestroyNotify)g_object_unref);
1536 #endif
1537 	}
1538 
1539 	return interface;
1540 }
1541 
1542 /**
1543  * tracker_db_manager_has_enough_space:
1544  *
1545  * Checks whether the file system, where the database files are stored,
1546  * has enough free space to allow modifications.
1547  *
1548  * returns: TRUE if there is enough space, FALSE otherwise
1549  **/
1550 gboolean
1551 tracker_db_manager_has_enough_space  (void)
1552 {
1553 	return tracker_file_system_has_enough_space (data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, FALSE);
1554 }
1555 
1556 
1557 inline static gchar *
1558 get_first_index_filename (void)
1559 {
1560 	return g_build_filename (g_get_user_cache_dir (),
1561 	                         "tracker",
1562 	                         FIRST_INDEX_FILENAME,
1563 	                         NULL);
1564 }
1565 
1566 /**
1567  * tracker_db_manager_get_first_index_done:
1568  *
1569  * Check if first full index of files was already done.
1570  *
1571  * Returns: %TRUE if a first full index have been done, %FALSE otherwise.
1572  **/
1573 gboolean
1574 tracker_db_manager_get_first_index_done (void)
1575 {
1576 	gboolean exists;
1577 	gchar *filename;
1578 
1579 	filename = get_first_index_filename ();
1580 	exists = g_file_test (filename, G_FILE_TEST_EXISTS);
1581 	g_free (filename);
1582 
1583 	return exists;
1584 }
1585 
1586 /**
1587  * tracker_db_manager_set_first_index_done:
1588  *
1589  * Set the status of the first full index of files. Should be set to
1590  *  %FALSE if the index was never done or if a reindex is needed. When
1591  *  the index is completed, should be set to %TRUE.
1592  **/
1593 void
1594 tracker_db_manager_set_first_index_done (gboolean done)
1595 {
1596 	gboolean already_exists;
1597 	gchar *filename;
1598 
1599 	filename = get_first_index_filename ();
1600 	already_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
1601 
1602 	if (done && !already_exists) {
1603 		GError *error = NULL;
1604 
1605 		/* If done, create stamp file if not already there */
1606 		if (!g_file_set_contents (filename, PACKAGE_VERSION, -1, &error)) {
1607 			g_warning ("  Could not create file:'%s' failed, %s",
1608 			           filename,
1609 			           error->message);
1610 			g_error_free (error);
1611 		} else {
1612 			g_message ("  First index file:'%s' created",
1613 			           filename);
1614 		}
1615 	} else if (!done && already_exists) {
1616 		/* If NOT done, remove stamp file */
1617 		g_message ("  Removing first index file:'%s'", filename);
1618 
1619 		if (g_remove (filename)) {
1620 			g_warning ("    Could not remove file:'%s', %s",
1621 			           filename,
1622 			           g_strerror (errno));
1623 		}
1624 	}
1625 
1626 	g_free (filename);
1627 }
1628 
1629 inline static gchar *
1630 get_last_crawl_filename (void)
1631 {
1632 	return g_build_filename (g_get_user_cache_dir (),
1633 	                         "tracker",
1634 	                         LAST_CRAWL_FILENAME,
1635 	                         NULL);
1636 }
1637 
1638 /**
1639  * tracker_db_manager_get_last_crawl_done:
1640  *
1641  * Check when last crawl was performed.
1642  *
1643  * Returns: time_t() value when last crawl occurred, otherwise 0.
1644  **/
1645 guint64
1646 tracker_db_manager_get_last_crawl_done (void)
1647 {
1648 	gchar *filename;
1649 	gchar *content;
1650 	guint64 then;
1651 
1652 	filename = get_last_crawl_filename ();
1653 
1654 	if (!g_file_get_contents (filename, &content, NULL, NULL)) {
1655 		g_message ("  No previous timestamp, crawling forced");
1656 		return 0;
1657 	}
1658 
1659 	then = g_ascii_strtoull (content, NULL, 10);
1660 	g_free (content);
1661 
1662 	return then;
1663 }
1664 
1665 /**
1666  * tracker_db_manager_set_last_crawl_done:
1667  *
1668  * Set the status of the first full index of files. Should be set to
1669  *  %FALSE if the index was never done or if a reindex is needed. When
1670  *  the index is completed, should be set to %TRUE.
1671  **/
1672 void
1673 tracker_db_manager_set_last_crawl_done (gboolean done)
1674 {
1675 	gboolean already_exists;
1676 	gchar *filename;
1677 
1678 	filename = get_last_crawl_filename ();
1679 	already_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
1680 
1681 	if (done && !already_exists) {
1682 		GError *error = NULL;
1683 		gchar *content;
1684 
1685 		content = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64) time (NULL));
1686 
1687 		/* If done, create stamp file if not already there */
1688 		if (!g_file_set_contents (filename, content, -1, &error)) {
1689 			g_warning ("  Could not create file:'%s' failed, %s",
1690 			           filename,
1691 			           error->message);
1692 			g_error_free (error);
1693 		} else {
1694 			g_message ("  Last crawl file:'%s' created",
1695 			           filename);
1696 		}
1697 
1698 		g_free (content);
1699 	} else if (!done && already_exists) {
1700 		/* If NOT done, remove stamp file */
1701 		g_message ("  Removing last crawl file:'%s'", filename);
1702 
1703 		if (g_remove (filename)) {
1704 			g_warning ("    Could not remove file:'%s', %s",
1705 			           filename,
1706 			           g_strerror (errno));
1707 		}
1708 	}
1709 
1710 	g_free (filename);
1711 }
1712 
1713 inline static gchar *
1714 get_need_mtime_check_filename (void)
1715 {
1716 	return g_build_filename (g_get_user_cache_dir (),
1717 	                         "tracker",
1718 	                         NEED_MTIME_CHECK_FILENAME,
1719 	                         NULL);
1720 }
1721 
1722 /**
1723  * tracker_db_manager_get_need_mtime_check:
1724  *
1725  * Check if the miner-fs was cleanly shutdown or not.
1726  *
1727  * Returns: %TRUE if we need to check mtimes for directories against
1728  * the database on the next start for the miner-fs, %FALSE otherwise.
1729  *
1730  * Since: 0.10
1731  **/
1732 gboolean
1733 tracker_db_manager_get_need_mtime_check (void)
1734 {
1735 	gboolean exists;
1736 	gchar *filename;
1737 
1738 	filename = get_need_mtime_check_filename ();
1739 	exists = g_file_test (filename, G_FILE_TEST_EXISTS);
1740 	g_free (filename);
1741 
1742 	/* Existence of the file means we cleanly shutdown before and
1743 	 * don't need to do the mtime check again on this start.
1744 	 */
1745 	return !exists;
1746 }
1747 
1748 /**
1749  * tracker_db_manager_set_need_mtime_check:
1750  * @needed: a #gboolean
1751  *
1752  * If the next start of miner-fs should perform a full mtime check
1753  * against each directory found and those in the database (for
1754  * complete synchronisation), then @needed should be #TRUE, otherwise
1755  * #FALSE.
1756  *
1757  * Creates a file in $HOME/.cache/tracker/ if an mtime check is not
1758  * needed. The idea behind this is that a check is forced if the file
1759  * is not cleaned up properly on shutdown (i.e. due to a crash or any
1760  * other uncontrolled shutdown reason).
1761  *
1762  * Since: 0.10
1763  **/
1764 void
1765 tracker_db_manager_set_need_mtime_check (gboolean needed)
1766 {
1767 	gboolean already_exists;
1768 	gchar *filename;
1769 
1770 	filename = get_need_mtime_check_filename ();
1771 	already_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
1772 
1773 	/* !needed = add file
1774 	 *  needed = remove file
1775 	 */
1776 	if (!needed && !already_exists) {
1777 		GError *error = NULL;
1778 
1779 		/* Create stamp file if not already there */
1780 		if (!g_file_set_contents (filename, PACKAGE_VERSION, -1, &error)) {
1781 			g_warning ("  Could not create file:'%s' failed, %s",
1782 			           filename,
1783 			           error->message);
1784 			g_error_free (error);
1785 		} else {
1786 			g_message ("  Need mtime check file:'%s' created",
1787 			           filename);
1788 		}
1789 	} else if (needed && already_exists) {
1790 		/* Remove stamp file */
1791 		g_message ("  Removing need mtime check file:'%s'", filename);
1792 
1793 		if (g_remove (filename)) {
1794 			g_warning ("    Could not remove file:'%s', %s",
1795 			           filename,
1796 			           g_strerror (errno));
1797 		}
1798 	}
1799 
1800 	g_free (filename);
1801 }
1802 
1803 void
1804 tracker_db_manager_lock (void)
1805 {
1806 	g_mutex_lock (&global_mutex);
1807 }
1808 
1809 gboolean
1810 tracker_db_manager_trylock (void)
1811 {
1812 	return g_mutex_trylock (&global_mutex);
1813 }
1814 
1815 void
1816 tracker_db_manager_unlock (void)
1817 {
1818 	g_mutex_unlock (&global_mutex);
1819 }