No issues found
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 |
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 }