1 /*
2 * Copyright (C) 2009, Nokia
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 * Author: Philip Van Hoof <philip@codeminded.be>
20 */
21
22 #include "config.h"
23
24 #include <string.h>
25 #include <glib.h>
26 #include <glib/gstdio.h>
27
28 #include <sqlite3.h>
29
30 #include <libtracker-data/tracker-ontology.h>
31 #include <libtracker-data/tracker-ontologies.h>
32 #include <libtracker-data/tracker-db-manager.h>
33 #include <libtracker-data/tracker-db-interface-sqlite.h>
34
35 #include "tracker-db-backup.h"
36
37 #define TRACKER_DB_BACKUP_META_FILENAME_T "meta-backup.db.tmp"
38
39 typedef struct {
40 GFile *destination;
41 TrackerDBBackupFinished callback;
42 gpointer user_data;
43 GDestroyNotify destroy;
44 GError *error;
45 } BackupInfo;
46
47 GQuark
48 tracker_db_backup_error_quark (void)
49 {
50 return g_quark_from_static_string ("tracker-db-backup-error-quark");
51 }
52
53 static gboolean
54 perform_callback (gpointer user_data)
55 {
56 BackupInfo *info = user_data;
57
58 if (info->callback) {
59 info->callback (info->error, info->user_data);
60 }
61
62 return FALSE;
63 }
64
65 static void
66 backup_info_free (gpointer user_data)
67 {
68 BackupInfo *info = user_data;
69
70 if (info->destination) {
71 g_object_unref (info->destination);
72 }
73
74 if (info->destroy) {
75 info->destroy (info->user_data);
76 }
77
78 g_clear_error (&info->error);
79
80 g_free (info);
81 }
82
83 static gboolean
84 backup_job (GIOSchedulerJob *job,
85 GCancellable *cancellable,
86 gpointer user_data)
87 {
88 BackupInfo *info = user_data;
89
90 const gchar *src_path;
91 GFile *parent_file, *temp_file;
92 gchar *temp_path;
93
94 sqlite3 *src_db = NULL;
95 sqlite3 *temp_db = NULL;
96 sqlite3_backup *backup = NULL;
97
98 src_path = tracker_db_manager_get_file (TRACKER_DB_METADATA);
99 parent_file = g_file_get_parent (info->destination);
100 temp_file = g_file_get_child (parent_file, TRACKER_DB_BACKUP_META_FILENAME_T);
101 g_file_delete (temp_file, NULL, NULL);
102 temp_path = g_file_get_path (temp_file);
103
104 if (sqlite3_open_v2 (src_path, &src_db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
105 g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
106 "Could not open sqlite3 database:'%s'", src_path);
107 }
108
109 if (!info->error && sqlite3_open (temp_path, &temp_db) != SQLITE_OK) {
110 g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
111 "Could not open sqlite3 database:'%s'", temp_path);
112 }
113
114 if (!info->error) {
115 backup = sqlite3_backup_init (temp_db, "main", src_db, "main");
116
117 if (!backup) {
118 g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
119 "Unable to initialize sqlite3 backup from '%s' to '%s'", src_path, temp_path);
120 }
121 }
122
123 if (!info->error && sqlite3_backup_step (backup, -1) != SQLITE_DONE) {
124 g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
125 "Unable to complete sqlite3 backup");
126 }
127
128 if (backup) {
129 if (sqlite3_backup_finish (backup) != SQLITE_OK) {
130 if (info->error) {
131 /* sqlite3_backup_finish can provide more detailed error message */
132 g_clear_error (&info->error);
133 }
134 g_set_error (&info->error,
135 TRACKER_DB_BACKUP_ERROR,
136 TRACKER_DB_BACKUP_ERROR_UNKNOWN,
137 "Unable to finish sqlite3 backup: %s",
138 sqlite3_errmsg (temp_db));
139 }
140 backup = NULL;
141 }
142
143 if (temp_db) {
144 sqlite3_close (temp_db);
145 temp_db = NULL;
146 }
147
148 if (src_db) {
149 sqlite3_close (src_db);
150 src_db = NULL;
151 }
152
153 if (!info->error) {
154 g_file_move (temp_file, info->destination,
155 G_FILE_COPY_OVERWRITE,
156 NULL, NULL, NULL,
157 &info->error);
158 }
159
160 g_free (temp_path);
161 g_object_unref (temp_file);
162 g_object_unref (parent_file);
163
164 g_idle_add_full (G_PRIORITY_DEFAULT, perform_callback, info,
165 backup_info_free);
166
167 return FALSE;
168 }
169
170 void
171 tracker_db_backup_save (GFile *destination,
172 TrackerDBBackupFinished callback,
173 gpointer user_data,
174 GDestroyNotify destroy)
175 {
176 BackupInfo *info = g_new0 (BackupInfo, 1);
177
178 info->destination = g_object_ref (destination);
179
180 info->callback = callback;
181 info->user_data = user_data;
182 info->destroy = destroy;
183
184 g_io_scheduler_push_job (backup_job, info, NULL, 0, NULL);
'g_io_scheduler_push_job' is deprecated (declared at /usr/include/glib-2.0/gio/gioscheduler.h:36): Use '"GThreadPool or g_task_run_in_thread"' instead
(emitted by gcc)
185 }