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-common.h>
37 #include <libtracker-common/tracker-parser.h>
38 
39 #include "tracker-db-manager.h"
40 #include "tracker-db-interface-sqlite.h"
41 #include "tracker-db-interface.h"
42 #include "tracker-data-manager.h"
43 #include "tracker-uuid.h"
44 
45 #define UNKNOWN_STATUS 0.5
46 
47 /* ZLib buffer settings */
48 #define ZLIB_BUF_SIZE                 8192
49 
50 #define MAX_INTERFACES_PER_CPU        16
51 #define MAX_INTERFACES                (MAX_INTERFACES_PER_CPU * g_get_num_processors ())
52 
53 /* Required minimum space needed to create databases (5Mb) */
54 #define TRACKER_DB_MIN_REQUIRED_SPACE 5242880
55 
56 /* Set current database version we are working with */
57 #define TRACKER_DB_VERSION_NOW        TRACKER_DB_VERSION_2_3
58 
59 #define TRACKER_VACUUM_CHECK_SIZE     ((goffset) 4 * 1024 * 1024 * 1024) /* 4GB */
60 
61 #define IN_USE_FILENAME               ".meta.isrunning"
62 
63 #define TOSTRING1(x) #x
64 #define TOSTRING(x) TOSTRING1(x)
65 #define TRACKER_PARSER_VERSION_STRING TOSTRING(TRACKER_PARSER_VERSION)
66 
67 #define FTS_FLAGS (TRACKER_DB_MANAGER_FTS_ENABLE_STEMMER |	  \
68                    TRACKER_DB_MANAGER_FTS_ENABLE_UNACCENT |	  \
69                    TRACKER_DB_MANAGER_FTS_ENABLE_STOP_WORDS |	  \
70                    TRACKER_DB_MANAGER_FTS_IGNORE_NUMBERS)
71 
72 typedef enum {
73 	TRACKER_DB_VERSION_UNKNOWN, /* Unknown */
74 	TRACKER_DB_VERSION_0_6_6,   /* before indexer-split */
75 	TRACKER_DB_VERSION_0_6_90,  /* after  indexer-split */
76 	TRACKER_DB_VERSION_0_6_91,  /* stable release */
77 	TRACKER_DB_VERSION_0_6_92,  /* current TRUNK */
78 	TRACKER_DB_VERSION_0_7_0,   /* vstore branch */
79 	TRACKER_DB_VERSION_0_7_4,   /* nothing special */
80 	TRACKER_DB_VERSION_0_7_12,  /* nmo ontology */
81 	TRACKER_DB_VERSION_0_7_13,  /* coalesce & writeback */
82 	TRACKER_DB_VERSION_0_7_17,  /* mlo ontology */
83 	TRACKER_DB_VERSION_0_7_20,  /* nco im ontology */
84 	TRACKER_DB_VERSION_0_7_21,  /* named graphs/localtime */
85 	TRACKER_DB_VERSION_0_7_22,  /* fts-limits branch */
86 	TRACKER_DB_VERSION_0_7_28,  /* RC1 + mto + nco:url */
87 	TRACKER_DB_VERSION_0_8_0,   /* stable release */
88 	TRACKER_DB_VERSION_0_9_0,   /* unstable release */
89 	TRACKER_DB_VERSION_0_9_8,   /* affiliation cardinality + volumes */
90 	TRACKER_DB_VERSION_0_9_15,  /* mtp:hidden */
91 	TRACKER_DB_VERSION_0_9_16,  /* Fix for NB#184823 */
92 	TRACKER_DB_VERSION_0_9_19,  /* collation */
93 	TRACKER_DB_VERSION_0_9_21,  /* Fix for NB#186055 */
94 	TRACKER_DB_VERSION_0_9_24,  /* nmo:PhoneMessage class */
95 	TRACKER_DB_VERSION_0_9_34,  /* ontology cache */
96 	TRACKER_DB_VERSION_0_9_38,  /* nie:url an inverse functional property */
97 	TRACKER_DB_VERSION_0_15_2,  /* fts4 */
98 	TRACKER_DB_VERSION_2_3      /* sparql1.1 */
99 } TrackerDBVersion;
100 
101 typedef struct {
102 	TrackerDBInterface *iface;
103 	const gchar        *file;
104 	const gchar        *name;
105 	gchar              *abs_filename;
106 	gint                cache_size;
107 	gint                page_size;
108 } TrackerDBDefinition;
109 
110 static TrackerDBDefinition db_base = {
111 	NULL,
112 	"meta.db",
113 	"meta",
114 	NULL,
115 	TRACKER_DB_CACHE_SIZE_DEFAULT,
116 	8192,
117 };
118 
119 struct _TrackerDBManager {
120 	GObject parent_instance;
121 	TrackerDBDefinition db;
122 	gboolean locations_initialized;
123 	gchar *data_dir;
124 	gchar *user_data_dir;
125 	gchar *in_use_filename;
126 	GFile *cache_location;
127 	gchar *shared_cache_key;
128 	TrackerDBManagerFlags flags;
129 	guint s_cache_size;
130 	guint u_cache_size;
131 
132 	gpointer vtab_data;
133 
134 	GWeakRef iface_data;
135 
136 	GAsyncQueue *interfaces;
137 };
138 
139 enum {
140 	SETUP_INTERFACE,
141 	UPDATE_INTERFACE,
142 	N_SIGNALS
143 };
144 
145 static guint signals[N_SIGNALS] = { 0 };
146 
147 G_DEFINE_TYPE (TrackerDBManager, tracker_db_manager, G_TYPE_OBJECT)
148 
149 static TrackerDBInterface *tracker_db_manager_create_db_interface   (TrackerDBManager    *db_manager,
150                                                                      gboolean             readonly,
151                                                                      GError             **error);
152 
153 static TrackerDBInterface * init_writable_db_interface              (TrackerDBManager *db_manager);
154 
155 TrackerDBManagerFlags
tracker_db_manager_get_flags(TrackerDBManager * db_manager,guint * select_cache_size,guint * update_cache_size)156 tracker_db_manager_get_flags (TrackerDBManager *db_manager,
157                               guint            *select_cache_size,
158                               guint            *update_cache_size)
159 {
160 	if (select_cache_size)
161 		*select_cache_size = db_manager->s_cache_size;
162 
163 	if (update_cache_size)
164 		*update_cache_size = db_manager->u_cache_size;
165 
166 	return db_manager->flags;
167 }
168 
169 static void
iface_set_params(TrackerDBInterface * iface,gboolean readonly,GError ** error)170 iface_set_params (TrackerDBInterface   *iface,
171                   gboolean              readonly,
172                   GError              **error)
173 {
174 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA encoding = \"UTF-8\"");
175 
176 	if (readonly) {
177 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = MEMORY;");
178 	} else {
179 		tracker_db_interface_execute_query (iface, NULL, "PRAGMA temp_store = FILE;");
180 	}
181 }
182 
183 static void
db_set_params(TrackerDBInterface * iface,const gchar * database,gint cache_size,gint page_size,gboolean enable_wal,GError ** error)184 db_set_params (TrackerDBInterface   *iface,
185                const gchar          *database,
186                gint                  cache_size,
187                gint                  page_size,
188                gboolean              enable_wal,
189                GError              **error)
190 {
191 	GError *internal_error = NULL;
192 	TrackerDBStatement *stmt;
193 
194 	TRACKER_NOTE (SQLITE, g_message ("  Setting page size to %d", page_size));
195 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".page_size = %d", database, page_size);
196 
197 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".synchronous = NORMAL", database);
198 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".auto_vacuum = 0", database);
199 
200 	if (enable_wal) {
201 		stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
202 		                                               &internal_error,
203 		                                               "PRAGMA \"%s\".journal_mode = WAL", database);
204 
205 		if (internal_error) {
206 			g_debug ("Can't set journal mode to WAL: '%s'",
207 			         internal_error->message);
208 			g_propagate_error (error, internal_error);
209 		} else {
210 			TrackerDBCursor *cursor;
211 
212 			cursor = tracker_db_statement_start_cursor (stmt, NULL);
213 			if (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
214 				if (g_ascii_strcasecmp (tracker_db_cursor_get_string (cursor, 0, NULL), "WAL") != 0) {
215 					g_set_error (error,
216 					             TRACKER_DB_INTERFACE_ERROR,
217 					             TRACKER_DB_OPEN_ERROR,
218 					             "Can't set journal mode to WAL");
219 				}
220 			}
221 			g_object_unref (cursor);
222 		}
223 
224 		g_clear_object (&stmt);
225 	}
226 
227 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".journal_size_limit = 10240000", database);
228 
229 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA \"%s\".cache_size = %d", database, cache_size);
230 	TRACKER_NOTE (SQLITE, g_message ("  Setting cache size to %d", cache_size));
231 }
232 
233 static gboolean
tracker_db_manager_get_metadata(TrackerDBManager * db_manager,const gchar * key,GValue * value)234 tracker_db_manager_get_metadata (TrackerDBManager   *db_manager,
235                                  const gchar        *key,
236                                  GValue             *value)
237 {
238 	TrackerDBInterface *iface;
239 	TrackerDBStatement *stmt;
240 	TrackerDBCursor *cursor;
241 
242 	iface = tracker_db_manager_get_writable_db_interface (db_manager);
243 	stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
244 	                                              NULL,
245 	                                              "SELECT value FROM metadata WHERE key = ?");
246 	if (!stmt)
247 		return FALSE;
248 
249 	tracker_db_statement_bind_text (stmt, 0, key);
250 	cursor = tracker_db_statement_start_cursor (stmt, NULL);
251 	g_object_unref (stmt);
252 
253 	if (!cursor || !tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
254 		g_clear_object (&cursor);
255 		return FALSE;
256 	}
257 
258 	tracker_db_cursor_get_value (cursor, 0, value);
259 	g_object_unref (cursor);
260 
261 	return G_VALUE_TYPE (value) != G_TYPE_INVALID;
262 }
263 
264 static void
tracker_db_manager_set_metadata(TrackerDBManager * db_manager,const gchar * key,GValue * value)265 tracker_db_manager_set_metadata (TrackerDBManager   *db_manager,
266 				 const gchar        *key,
267 				 GValue             *value)
268 {
269 	TrackerDBInterface *iface;
270 	TrackerDBStatement *stmt;
271 	GError *error = NULL;
272 
273 	iface = tracker_db_manager_get_writable_db_interface (db_manager);
274 	stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
275 	                                              &error,
276 	                                              "INSERT OR REPLACE INTO metadata VALUES (?, ?)");
277 	if (stmt) {
278 		tracker_db_statement_bind_text (stmt, 0, key);
279 		tracker_db_statement_bind_value (stmt, 1, value);
280 		tracker_db_statement_execute (stmt, &error);
281 
282 		g_object_unref (stmt);
283 	}
284 
285 	if (error) {
286 		g_critical ("Could not store database metadata: %s\n", error->message);
287 		g_error_free (error);
288 	}
289 }
290 
291 static TrackerDBVersion
db_get_version(TrackerDBManager * db_manager)292 db_get_version (TrackerDBManager *db_manager)
293 {
294 	TrackerDBInterface *iface;
295 	TrackerDBStatement *stmt;
296 	TrackerDBCursor *cursor;
297 	TrackerDBVersion version;
298 
299 	iface = tracker_db_manager_get_writable_db_interface (db_manager);
300 	stmt = tracker_db_interface_create_statement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
301 	                                              NULL, "PRAGMA user_version");
302 	if (!stmt)
303 		return TRACKER_DB_VERSION_UNKNOWN;
304 
305 	cursor = tracker_db_statement_start_cursor (stmt, NULL);
306 	g_object_unref (stmt);
307 
308 	if (!cursor || !tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
309 		g_clear_object (&cursor);
310 		return TRACKER_DB_VERSION_UNKNOWN;
311 	}
312 
313 	version = tracker_db_cursor_get_int (cursor, 0);
314 	g_object_unref (cursor);
315 
316 	return version;
317 }
318 
319 static void
tracker_db_manager_update_version(TrackerDBManager * db_manager)320 tracker_db_manager_update_version (TrackerDBManager *db_manager)
321 {
322 	TrackerDBInterface *iface;
323 	TrackerDBStatement *stmt;
324 	GError *error = NULL;
325 
326 	iface = tracker_db_manager_get_writable_db_interface (db_manager);
327 	stmt = tracker_db_interface_create_vstatement (iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
328 	                                               &error, "PRAGMA user_version = %d",
329 	                                               TRACKER_DB_VERSION_NOW);
330 	if (stmt) {
331 		tracker_db_statement_execute (stmt, &error);
332 		g_object_unref (stmt);
333 	}
334 
335 	if (error) {
336 		g_critical ("Could not set database version: %s\n", error->message);
337 		g_error_free (error);
338 	}
339 }
340 
341 static gchar *
db_get_locale(TrackerDBManager * db_manager)342 db_get_locale (TrackerDBManager *db_manager)
343 {
344 	GValue value = G_VALUE_INIT;
345 	gchar *locale = NULL;
346 
347 	if (!tracker_db_manager_get_metadata (db_manager, "locale", &value))
348 		return NULL;
349 
350 	locale = g_value_dup_string (&value);
351 	g_value_unset (&value);
352 
353 	return locale;
354 }
355 
356 static void
db_set_locale(TrackerDBManager * db_manager,const gchar * locale)357 db_set_locale (TrackerDBManager *db_manager,
358 	       const gchar      *locale)
359 {
360 	GValue value = G_VALUE_INIT;
361 
362 	g_value_init (&value, G_TYPE_STRING);
363 	g_value_set_string (&value, locale);
364 
365 	tracker_db_manager_set_metadata (db_manager, "locale", &value);
366 	g_value_unset (&value);
367 }
368 
369 gboolean
tracker_db_manager_locale_changed(TrackerDBManager * db_manager,GError ** error)370 tracker_db_manager_locale_changed (TrackerDBManager  *db_manager,
371                                    GError           **error)
372 {
373 	gchar *db_locale;
374 	gchar *current_locale;
375 	gboolean changed;
376 
377 	/* Get current collation locale */
378 	current_locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
379 
380 	/* Get db locale */
381 	db_locale = db_get_locale (db_manager);
382 
383 	/* If they are different, recreate indexes. Note that having
384 	 * both to NULL is actually valid, they would default to
385 	 * the unicode collation without locale-specific stuff. */
386 	if (g_strcmp0 (db_locale, current_locale) != 0) {
387 		g_set_error (error,
388 		             TRACKER_DB_INTERFACE_ERROR,
389 		             TRACKER_DB_OPEN_ERROR,
390 		             "Locale change detected (DB:%s, User/App:%s)",
391 		             db_locale ? db_locale : "unknown",
392 		             current_locale);
393 		changed = TRUE;
394 	} else {
395 		g_debug ("Current and DB locales match: '%s'", db_locale);
396 		changed = FALSE;
397 	}
398 
399 	g_free (db_locale);
400 	g_free (current_locale);
401 
402 	return changed;
403 }
404 
405 void
tracker_db_manager_set_current_locale(TrackerDBManager * db_manager)406 tracker_db_manager_set_current_locale (TrackerDBManager *db_manager)
407 {
408 	gchar *current_locale;
409 
410 	/* Get current collation locale */
411 	current_locale = tracker_locale_get (TRACKER_LOCALE_COLLATE);
412 	g_debug ("Saving DB locale as: '%s'", current_locale);
413 	db_set_locale (db_manager, current_locale);
414 	g_free (current_locale);
415 }
416 
417 static void
tracker_db_manager_ensure_location(TrackerDBManager * db_manager,GFile * cache_location)418 tracker_db_manager_ensure_location (TrackerDBManager *db_manager,
419 				    GFile            *cache_location)
420 {
421 	gchar *dir;
422 
423 	if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0) {
424 		return;
425 	}
426 
427 	if (db_manager->locations_initialized) {
428 		return;
429 	}
430 
431 	db_manager->locations_initialized = TRUE;
432 	db_manager->data_dir = g_file_get_path (cache_location);
433 
434 	db_manager->db = db_base;
435 
436 	dir = g_file_get_path (cache_location);
437 	db_manager->db.abs_filename = g_build_filename (dir, db_manager->db.file, NULL);
438 	g_free (dir);
439 }
440 
441 gboolean
tracker_db_manager_db_exists(GFile * cache_location)442 tracker_db_manager_db_exists (GFile *cache_location)
443 {
444 	gchar *dir;
445 	gchar *filename;
446 	gboolean db_exists = FALSE;
447 
448 	dir = g_file_get_path (cache_location);
449 	filename = g_build_filename (dir, db_base.file, NULL);
450 
451 	db_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
452 
453 	g_free (dir);
454 	g_free (filename);
455 
456 	return db_exists;
457 }
458 
459 static gboolean
db_check_integrity(TrackerDBManager * db_manager)460 db_check_integrity (TrackerDBManager *db_manager)
461 {
462 	GError *internal_error = NULL;
463 	TrackerDBStatement *stmt;
464 	TrackerDBCursor *cursor = NULL;
465 
466 	stmt = tracker_db_interface_create_statement (db_manager->db.iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
467 	                                              &internal_error,
468 	                                              "PRAGMA integrity_check(1)");
469 
470 	if (!stmt) {
471 		if (internal_error) {
472 			g_message ("Corrupt database: failed to create integrity_check statement: %s", internal_error->message);
473 		}
474 
475 		g_clear_error (&internal_error);
476 		return FALSE;
477 	}
478 
479 	cursor = tracker_db_statement_start_cursor (stmt, NULL);
480 	g_object_unref (stmt);
481 
482 	if (cursor) {
483 		if (tracker_db_cursor_iter_next (cursor, NULL, NULL)) {
484 			const gchar *check_result;
485 
486 			check_result = tracker_db_cursor_get_string (cursor, 0, NULL);
487 			if (g_strcmp0 (check_result, "ok") != 0) {
488 				g_message ("Corrupt database: sqlite integrity check returned '%s'", check_result);
489 				return FALSE;
490 			}
491 		}
492 		g_object_unref (cursor);
493 	}
494 
495 	/* ensure that database has been initialized by an earlier tracker-store start
496 	   by checking whether Resource table exists */
497 	stmt = tracker_db_interface_create_statement (db_manager->db.iface, TRACKER_DB_STATEMENT_CACHE_TYPE_NONE,
498 	                                              &internal_error,
499 	                                              "SELECT 1 FROM Resource");
500 	if (!stmt) {
501 		if (internal_error) {
502 			g_message ("Corrupt database: failed to create resource check statement: %s", internal_error->message);
503 		}
504 
505 		g_clear_error (&internal_error);
506 		return FALSE;
507 	}
508 
509 	g_object_unref (stmt);
510 
511 	return TRUE;
512 }
513 
514 TrackerDBManager *
tracker_db_manager_new(TrackerDBManagerFlags flags,GFile * cache_location,gboolean * first_time,gboolean shared_cache,guint select_cache_size,guint update_cache_size,TrackerBusyCallback busy_callback,gpointer busy_user_data,GObject * iface_data,gpointer vtab_data,GError ** error)515 tracker_db_manager_new (TrackerDBManagerFlags   flags,
516 			GFile                  *cache_location,
517 			gboolean               *first_time,
518 			gboolean                shared_cache,
519 			guint                   select_cache_size,
520 			guint                   update_cache_size,
521 			TrackerBusyCallback     busy_callback,
522 			gpointer                busy_user_data,
523                         GObject                *iface_data,
524                         gpointer                vtab_data,
525 			GError                **error)
526 {
527 	TrackerDBManager *db_manager;
528 	TrackerDBVersion version;
529 	int in_use_file;
530 	TrackerDBInterface *resources_iface;
531 	GError *internal_error = NULL;
532 	gboolean need_to_create = FALSE;
533 
534 	db_manager = g_object_new (TRACKER_TYPE_DB_MANAGER, NULL);
535 	db_manager->vtab_data = vtab_data;
536 
537 	/* First set defaults for return values */
538 	if (first_time) {
539 		*first_time = FALSE;
540 	}
541 
542 	/* Set up locations */
543 	db_manager->flags = flags;
544 	db_manager->s_cache_size = select_cache_size;
545 	db_manager->u_cache_size = update_cache_size;
546 	db_manager->interfaces = g_async_queue_new_full (g_object_unref);
547 
548 	g_set_object (&db_manager->cache_location, cache_location);
549 	g_weak_ref_init (&db_manager->iface_data, iface_data);
550 
551 	if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) == 0) {
552 		tracker_db_manager_ensure_location (db_manager, cache_location);
553 		db_manager->in_use_filename = g_build_filename (db_manager->data_dir,
554 		                                                IN_USE_FILENAME,
555 		                                                NULL);
556 
557 		/* Don't do need_reindex checks for readonly (direct-access) */
558 		if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
559 
560 			/* Make sure the directories exist */
561 			g_mkdir_with_parents (db_manager->data_dir, 00755);
562 		}
563 	} else {
564 		db_manager->shared_cache_key = tracker_generate_uuid (NULL);
565 	}
566 
567 	if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0) {
568 		need_to_create = TRUE;
569 	} else if (!g_file_test (db_manager->db.abs_filename, G_FILE_TEST_EXISTS)) {
570 		if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
571 			TRACKER_NOTE (SQLITE, g_message ("Could not find database file:'%s', will create it.", db_manager->db.abs_filename));
572 			need_to_create = TRUE;
573 		} else {
574 			g_set_error (error,
575 			             TRACKER_DB_INTERFACE_ERROR,
576 			             TRACKER_DB_OPEN_ERROR,
577 			             "Could not find database file:'%s'.", db_manager->db.abs_filename);
578 
579 			g_object_unref (db_manager);
580 			return NULL;
581 		}
582 	} else if ((flags & TRACKER_DB_MANAGER_SKIP_VERSION_CHECK) == 0) {
583 		version = db_get_version (db_manager);
584 
585 		if (version < TRACKER_DB_VERSION_NOW) {
586 			g_set_error (error,
587 			             TRACKER_DB_INTERFACE_ERROR,
588 			             TRACKER_DB_OPEN_ERROR,
589 			             "Database version is too old: got version %i, but %i is needed",
590 			             version, TRACKER_DB_VERSION_NOW);
591 
592 			g_object_unref (db_manager);
593 			return NULL;
594 		}
595 	}
596 
597 	db_manager->locations_initialized = TRUE;
598 
599 	if ((flags & TRACKER_DB_MANAGER_READONLY) != 0) {
600 		GValue value = G_VALUE_INIT;
601 		TrackerDBManagerFlags fts_flags = 0;
602 
603 		if (tracker_db_manager_get_metadata (db_manager, "fts-flags", &value)) {
604 			fts_flags = g_ascii_strtoll (g_value_get_string (&value), NULL, 10);
605 			g_value_unset (&value);
606 		}
607 
608 		/* Readonly connections should go with the FTS flags as stored
609 		 * in metadata.
610 		 */
611 		db_manager->flags = (db_manager->flags & ~(FTS_FLAGS)) | fts_flags;
612 	}
613 
614 	/* Set general database options */
615 	if (shared_cache) {
616 		TRACKER_NOTE (SQLITE, g_message ("Enabling database shared cache"));
617 		tracker_db_interface_sqlite_enable_shared_cache ();
618 	}
619 
620 	if (need_to_create) {
621 		if (first_time) {
622 			*first_time = TRUE;
623 		}
624 
625 		if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) == 0 &&
626 		     !tracker_file_system_has_enough_space (db_manager->data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, TRUE)) {
627 			g_set_error (error,
628 			             TRACKER_DB_INTERFACE_ERROR,
629 			             TRACKER_DB_OPEN_ERROR,
630 			             "Filesystem does not have enough space");
631 			return FALSE;
632 		}
633 
634 		TRACKER_NOTE (SQLITE, g_message ("Creating database files for %s...", db_manager->db.abs_filename));
635 
636 		db_manager->db.iface = tracker_db_manager_create_db_interface (db_manager, FALSE, &internal_error);
637 		if (internal_error) {
638 			g_propagate_error (error, internal_error);
639 			g_object_unref (db_manager);
640 			return FALSE;
641 		}
642 
643 		g_clear_object (&db_manager->db.iface);
644 
645 		tracker_db_manager_update_version (db_manager);
646 	} else {
647 		TRACKER_NOTE (SQLITE, g_message ("Loading files for database %s...", db_manager->db.abs_filename));
648 
649 		if ((flags & TRACKER_DB_MANAGER_READONLY) == 0) {
650 			/* Check that the database was closed cleanly and do a deeper integrity
651 			 * check if it wasn't, raising an error if we detect corruption. */
652 
653 			if (g_file_test (db_manager->in_use_filename, G_FILE_TEST_EXISTS)) {
654 				gsize size = 0;
655 				struct stat st;
656 
657 				TRACKER_NOTE (SQLITE, g_message ("Didn't shut down cleanly last time, doing integrity checks"));
658 
659 				if (g_stat (db_manager->db.abs_filename, &st) == 0) {
660 					size = st.st_size;
661 				}
662 
663 				/* Size is 1 when using echo > file.db, none of our databases
664 				 * are only one byte in size even initually. */
665 				if (size <= 1) {
666 					g_debug ("Database is corrupt: size is 1 byte or less.");
667 					return FALSE;
668 				}
669 
670 				db_manager->db.iface = tracker_db_manager_create_db_interface (db_manager, FALSE, &internal_error);
671 
672 				if (internal_error) {
673 					/* If this already doesn't succeed, then surely the file is
674 					 * corrupt. No need to check for integrity anymore. */
675 					g_propagate_error (error, internal_error);
676 					g_object_unref (db_manager);
677 					return NULL;
678 				}
679 
680 				busy_callback ("Integrity checking", 0, busy_user_data);
681 
682 				if (db_check_integrity (db_manager) == FALSE) {
683 					g_set_error (error,
684 					             TRACKER_DB_INTERFACE_ERROR,
685 					             TRACKER_DB_OPEN_ERROR,
686 					             "Corrupt db file");
687 					g_object_unref (db_manager);
688 					return NULL;
689 				}
690 			}
691 		}
692 	}
693 
694 	if ((flags & (TRACKER_DB_MANAGER_READONLY | TRACKER_DB_MANAGER_IN_MEMORY)) == 0) {
695 		/* do not create in-use file for read-only mode (direct access) */
696 		in_use_file = g_open (db_manager->in_use_filename,
697 			              O_WRONLY | O_APPEND | O_CREAT | O_SYNC,
698 			              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
699 
700 		if (in_use_file >= 0) {
701 		        fsync (in_use_file);
702 		        close (in_use_file);
703 		}
704 	}
705 
706 	resources_iface = tracker_db_manager_create_db_interface (db_manager,
707 	                                                          TRUE, &internal_error);
708 
709 	if (internal_error) {
710 		g_propagate_error (error, internal_error);
711 		g_object_unref (db_manager);
712 		return NULL;
713 	}
714 
715 	g_clear_object (&resources_iface);
716 
717 	return db_manager;
718 }
719 
720 void
tracker_db_manager_finalize(GObject * object)721 tracker_db_manager_finalize (GObject *object)
722 {
723 	TrackerDBManager *db_manager = TRACKER_DB_MANAGER (object);
724 	gboolean readonly = (db_manager->flags & TRACKER_DB_MANAGER_READONLY) != 0;
725 
726 	g_async_queue_unref (db_manager->interfaces);
727 	g_free (db_manager->db.abs_filename);
728 
729 	if (db_manager->db.iface) {
730 		if (!readonly)
731 			tracker_db_interface_sqlite_wal_checkpoint (db_manager->db.iface, TRUE, NULL);
732 		g_object_unref (db_manager->db.iface);
733 	}
734 
735 	g_weak_ref_clear (&db_manager->iface_data);
736 
737 	g_free (db_manager->data_dir);
738 
739 	if (db_manager->in_use_filename && !readonly) {
740 		/* do not delete in-use file for read-only mode (direct access) */
741 		g_unlink (db_manager->in_use_filename);
742 	}
743 
744 	g_free (db_manager->in_use_filename);
745 	g_free (db_manager->shared_cache_key);
746 
747 	G_OBJECT_CLASS (tracker_db_manager_parent_class)->finalize (object);
748 }
749 
750 static TrackerDBInterface *
tracker_db_manager_create_db_interface(TrackerDBManager * db_manager,gboolean readonly,GError ** error)751 tracker_db_manager_create_db_interface (TrackerDBManager  *db_manager,
752                                         gboolean           readonly,
753                                         GError           **error)
754 {
755 	TrackerDBInterface *connection;
756 	GError *internal_error = NULL;
757 	TrackerDBInterfaceFlags flags = 0;
758 
759 	if (readonly)
760 		flags |= TRACKER_DB_INTERFACE_READONLY;
761 	if (db_manager->flags & TRACKER_DB_MANAGER_ENABLE_MUTEXES)
762 		flags |= TRACKER_DB_INTERFACE_USE_MUTEX;
763 	if (db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY)
764 		flags |= TRACKER_DB_INTERFACE_IN_MEMORY;
765 
766 	connection = tracker_db_interface_sqlite_new (db_manager->db.abs_filename,
767 	                                              db_manager->shared_cache_key,
768 	                                              flags,
769 	                                              &internal_error);
770 	if (internal_error) {
771 		g_propagate_error (error, internal_error);
772 		return NULL;
773 	}
774 
775 	tracker_db_interface_set_user_data (connection,
776 	                                    g_weak_ref_get (&db_manager->iface_data),
777 	                                    g_object_unref);
778 
779 	if (db_manager->vtab_data)
780 		tracker_db_interface_init_vtabs (connection, db_manager->vtab_data);
781 
782 	iface_set_params (connection,
783 	                  readonly,
784 	                  &internal_error);
785 	db_set_params (connection, "main",
786 	               db_manager->db.cache_size,
787 	               db_manager->db.page_size,
788 	               !(db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY),
789 	               &internal_error);
790 
791 	if (internal_error) {
792 		g_propagate_error (error, internal_error);
793 		g_object_unref (connection);
794 		return NULL;
795 	}
796 
797 	tracker_db_interface_set_max_stmt_cache_size (connection,
798 	                                              TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT,
799 	                                              db_manager->s_cache_size);
800 
801 	if (!readonly) {
802 		tracker_db_interface_set_max_stmt_cache_size (connection,
803 		                                              TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
804 		                                              db_manager->u_cache_size);
805 	}
806 
807 	return connection;
808 }
809 
810 /**
811  * tracker_db_manager_get_db_interface:
812  *
813  * Request a database connection to the database
814  *
815  * The caller must NOT g_object_unref the result
816  *
817  * returns: (callee-owns): a database connection
818  **/
819 TrackerDBInterface *
tracker_db_manager_get_db_interface(TrackerDBManager * db_manager)820 tracker_db_manager_get_db_interface (TrackerDBManager *db_manager)
821 {
822 	GError *internal_error = NULL;
823 	TrackerDBInterface *interface = NULL;
824 	guint len, i;
825 
826 	/* The interfaces never actually leave the async queue,
827 	 * we use it as a thread synchronized LRU, which doesn't
828 	 * mean the interface found has no other active cursors,
829 	 * in which case we either optionally create a new
830 	 * TrackerDBInterface, or resign to sharing the obtained
831 	 * one with other threads (thus getting increased contention
832 	 * in the interface lock).
833 	 */
834 	g_async_queue_lock (db_manager->interfaces);
835 	len = g_async_queue_length_unlocked (db_manager->interfaces);
836 
837 	/* 1st. Find a free interface */
838 	for (i = 0; i < len; i++) {
839 		interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
840 
841 		if (!interface)
842 			break;
843 		if (!tracker_db_interface_get_is_used (interface))
844 			break;
845 
846 		g_async_queue_push_unlocked (db_manager->interfaces, interface);
847 		interface = NULL;
848 	}
849 
850 	/* 2nd. If no more interfaces can be created, pick one */
851 	if (!interface && len >= MAX_INTERFACES) {
852 		interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
853 	}
854 
855 	if (interface) {
856 		g_signal_emit (db_manager, signals[UPDATE_INTERFACE], 0, interface);
857 	} else {
858 		/* 3rd. Create a new interface to satisfy the request */
859 		interface = tracker_db_manager_create_db_interface (db_manager,
860 		                                                    TRUE, &internal_error);
861 
862 		if (interface) {
863 			g_signal_emit (db_manager, signals[SETUP_INTERFACE], 0, interface);
864 		} else {
865 			if (g_async_queue_length_unlocked (db_manager->interfaces) == 0) {
866 				g_critical ("Error opening database: %s", internal_error->message);
867 				g_error_free (internal_error);
868 				g_async_queue_unlock (db_manager->interfaces);
869 				return NULL;
870 			} else {
871 				g_error_free (internal_error);
872 				/* Fetch the first interface back. Oh well */
873 				interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
874 			}
875 		}
876 	}
877 
878 	tracker_db_interface_ref_use (interface);
879 
880 	g_async_queue_push_unlocked (db_manager->interfaces, interface);
881 	g_async_queue_unlock (db_manager->interfaces);
882 
883 	return interface;
884 }
885 
886 static void
tracker_db_manager_init(TrackerDBManager * manager)887 tracker_db_manager_init (TrackerDBManager *manager)
888 {
889 }
890 
891 static void
tracker_db_manager_class_init(TrackerDBManagerClass * klass)892 tracker_db_manager_class_init (TrackerDBManagerClass *klass)
893 {
894 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
895 
896 	object_class->finalize = tracker_db_manager_finalize;
897 
898 	signals[SETUP_INTERFACE] =
899                g_signal_new ("setup-interface",
900                              G_TYPE_FROM_CLASS (klass),
901                              G_SIGNAL_RUN_LAST, 0,
902                              NULL, NULL,
903                              g_cclosure_marshal_VOID__OBJECT,
904                              G_TYPE_NONE,
905                              1, TRACKER_TYPE_DB_INTERFACE);
906 	signals[UPDATE_INTERFACE] =
907                g_signal_new ("update-interface",
908                              G_TYPE_FROM_CLASS (klass),
909                              G_SIGNAL_RUN_LAST, 0,
910                              NULL, NULL,
911                              g_cclosure_marshal_VOID__OBJECT,
912                              G_TYPE_NONE,
913                              1, TRACKER_TYPE_DB_INTERFACE);
914 }
915 
916 static TrackerDBInterface *
init_writable_db_interface(TrackerDBManager * db_manager)917 init_writable_db_interface (TrackerDBManager *db_manager)
918 {
919 	TrackerDBInterface *iface;
920 	GError *error = NULL;
921 	gboolean readonly;
922 
923 	/* Honor anyway the DBManager readonly flag */
924 	readonly = (db_manager->flags & TRACKER_DB_MANAGER_READONLY) != 0;
925 	iface = tracker_db_manager_create_db_interface (db_manager, readonly, &error);
926 	if (error) {
927 		g_critical ("Error opening readwrite database: %s", error->message);
928 		g_error_free (error);
929 	}
930 
931 	return iface;
932 }
933 
934 TrackerDBInterface *
tracker_db_manager_get_writable_db_interface(TrackerDBManager * db_manager)935 tracker_db_manager_get_writable_db_interface (TrackerDBManager *db_manager)
936 {
937 	if (db_manager->db.iface == NULL)
938 		db_manager->db.iface = init_writable_db_interface (db_manager);
939 
940 	return db_manager->db.iface;
941 }
942 
943 /**
944  * tracker_db_manager_has_enough_space:
945  *
946  * Checks whether the file system, where the database files are stored,
947  * has enough free space to allow modifications.
948  *
949  * returns: TRUE if there is enough space, FALSE otherwise
950  **/
951 gboolean
tracker_db_manager_has_enough_space(TrackerDBManager * db_manager)952 tracker_db_manager_has_enough_space (TrackerDBManager *db_manager)
953 {
954 	if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0)
955 		return TRUE;
956 	return tracker_file_system_has_enough_space (db_manager->data_dir, TRACKER_DB_MIN_REQUIRED_SPACE, FALSE);
957 }
958 
959 gboolean
tracker_db_manager_get_tokenizer_changed(TrackerDBManager * db_manager)960 tracker_db_manager_get_tokenizer_changed (TrackerDBManager *db_manager)
961 {
962 	GValue value = G_VALUE_INIT;
963 	const gchar *version;
964 	TrackerDBManagerFlags flags;
965 	gboolean changed;
966 
967 	if (!tracker_db_manager_get_metadata (db_manager, "fts-flags", &value))
968 		return TRUE;
969 
970 	flags = g_ascii_strtoll (g_value_get_string (&value), NULL, 10);
971 	g_value_unset (&value);
972 
973 	if ((db_manager->flags & TRACKER_DB_MANAGER_READONLY) == 0 &&
974 	    flags != (db_manager->flags & FTS_FLAGS)) {
975 		return TRUE;
976 	}
977 
978 	if (!tracker_db_manager_get_metadata (db_manager, "parser-version", &value))
979 		return TRUE;
980 
981 	version = g_value_get_string (&value);
982 	changed = strcmp (version, TRACKER_PARSER_VERSION_STRING) != 0;
983 	g_value_unset (&value);
984 
985 	return changed;
986 }
987 
988 void
tracker_db_manager_tokenizer_update(TrackerDBManager * db_manager)989 tracker_db_manager_tokenizer_update (TrackerDBManager *db_manager)
990 {
991 	GValue value = G_VALUE_INIT;
992 
993 	g_value_init (&value, G_TYPE_STRING);
994 	g_value_set_string (&value, TRACKER_PARSER_VERSION_STRING);
995 	tracker_db_manager_set_metadata (db_manager, "parser-version", &value);
996 	g_value_unset (&value);
997 
998 	g_value_init (&value, G_TYPE_INT64);
999 	g_value_set_int64 (&value, (db_manager->flags & FTS_FLAGS));
1000 	tracker_db_manager_set_metadata (db_manager, "fts-flags", &value);
1001 	g_value_unset (&value);
1002 }
1003 
1004 void
tracker_db_manager_check_perform_vacuum(TrackerDBManager * db_manager)1005 tracker_db_manager_check_perform_vacuum (TrackerDBManager *db_manager)
1006 {
1007 	TrackerDBInterface *iface;
1008 
1009 	if ((db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY) != 0)
1010 		return;
1011 	if (tracker_file_get_size (db_manager->db.abs_filename) < TRACKER_VACUUM_CHECK_SIZE)
1012 		return;
1013 
1014 	iface = tracker_db_manager_get_writable_db_interface (db_manager);
1015 	tracker_db_interface_execute_query (iface, NULL, "VACUUM");
1016 }
1017 
1018 static gboolean
ensure_create_database_file(TrackerDBManager * db_manager,GFile * file,GError ** error)1019 ensure_create_database_file (TrackerDBManager  *db_manager,
1020                              GFile             *file,
1021                              GError           **error)
1022 {
1023 	TrackerDBInterface *iface;
1024 	GError *file_error = NULL;
1025 	gchar *path;
1026 
1027 	/* Ensure the database is created from scratch */
1028 	if (!g_file_delete (file, NULL, &file_error)) {
1029 		if (!g_error_matches (file_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1030 			g_propagate_error (error, file_error);
1031 			return FALSE;
1032 		}
1033 
1034 		g_clear_error (&file_error);
1035 	}
1036 
1037 	/* Create the database file in a separate interface, so we can
1038 	 * configure page_size and journal_mode outside a transaction, so
1039 	 * they apply throughout the whole lifetime.
1040 	 */
1041 	path = g_file_get_path (file);
1042 	iface = tracker_db_interface_sqlite_new (path,
1043 	                                         db_manager->shared_cache_key,
1044 	                                         0, error);
1045 	g_free (path);
1046 
1047 	if (!iface)
1048 		return FALSE;
1049 
1050 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA cache_size = %d",
1051 	                                    db_manager->db.page_size);
1052 	tracker_db_interface_execute_query (iface, NULL, "PRAGMA journal_mode = WAL");
1053 
1054 	g_object_unref (iface);
1055 	return TRUE;
1056 }
1057 
1058 gboolean
tracker_db_manager_attach_database(TrackerDBManager * db_manager,TrackerDBInterface * iface,const gchar * name,gboolean create,GError ** error)1059 tracker_db_manager_attach_database (TrackerDBManager    *db_manager,
1060                                     TrackerDBInterface  *iface,
1061                                     const gchar         *name,
1062                                     gboolean             create,
1063                                     GError             **error)
1064 {
1065 	gchar *filename, *escaped;
1066 	GFile *file = NULL;
1067 
1068 	if (db_manager->cache_location) {
1069 		filename = g_strdup_printf ("%s.db", name);
1070 		escaped = g_uri_escape_string (filename, NULL, FALSE);
1071 		file = g_file_get_child (db_manager->cache_location, escaped);
1072 		g_free (filename);
1073 		g_free (escaped);
1074 
1075 		if (create) {
1076 			if (!ensure_create_database_file (db_manager, file, error)) {
1077 				g_object_unref (file);
1078 				return FALSE;
1079 			}
1080 		}
1081 	}
1082 
1083 	if (!tracker_db_interface_attach_database (iface, file, name, error)) {
1084 		g_clear_object (&file);
1085 		return FALSE;
1086 	}
1087 
1088 	g_clear_object (&file);
1089 	db_set_params (iface, name,
1090 	               db_manager->db.cache_size,
1091 	               db_manager->db.page_size,
1092 	               !(db_manager->flags & TRACKER_DB_MANAGER_IN_MEMORY),
1093 	               error);
1094 	return TRUE;
1095 }
1096 
1097 gboolean
tracker_db_manager_detach_database(TrackerDBManager * db_manager,TrackerDBInterface * iface,const gchar * name,GError ** error)1098 tracker_db_manager_detach_database (TrackerDBManager    *db_manager,
1099                                     TrackerDBInterface  *iface,
1100                                     const gchar         *name,
1101                                     GError             **error)
1102 {
1103 	return tracker_db_interface_detach_database (iface, name, error);
1104 }
1105 
1106 void
tracker_db_manager_release_memory(TrackerDBManager * db_manager)1107 tracker_db_manager_release_memory (TrackerDBManager *db_manager)
1108 {
1109 	TrackerDBInterface *iface;
1110 	gint i, len;
1111 
1112 	g_async_queue_lock (db_manager->interfaces);
1113 	len = g_async_queue_length_unlocked (db_manager->interfaces);
1114 
1115 	for (i = 0; i < len; i++) {
1116 		iface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
1117 		if (!iface)
1118 			break;
1119 
1120 		if (tracker_db_interface_get_is_used (iface))
1121 			g_async_queue_push_unlocked (db_manager->interfaces, iface);
1122 		else
1123 			g_object_unref (iface);
1124 	}
1125 
1126 	if (g_async_queue_length_unlocked (db_manager->interfaces) < len) {
1127 		g_debug ("Freed %d readonly interfaces",
1128 		         len - g_async_queue_length_unlocked (db_manager->interfaces));
1129 	}
1130 
1131 	if (db_manager->db.iface) {
1132 		gssize bytes;
1133 
1134 		bytes = tracker_db_interface_sqlite_release_memory (db_manager->db.iface);
1135 
1136 		if (bytes > 0) {
1137 			g_debug ("Freed %" G_GSSIZE_MODIFIER "d bytes from writable interface",
1138 			         bytes);
1139 		}
1140 	}
1141 
1142 	g_async_queue_unlock (db_manager->interfaces);
1143 }
1144