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 	GFile *file;
42 	TrackerDBBackupFinished callback;
43 	gpointer user_data;
44 	GDestroyNotify destroy;
45 	GError *error;
46 } BackupInfo;
47 
48 GQuark
tracker_db_backup_error_quark(void)49 tracker_db_backup_error_quark (void)
50 {
51 	return g_quark_from_static_string ("tracker-db-backup-error-quark");
52 }
53 
54 static gboolean
perform_callback(gpointer user_data)55 perform_callback (gpointer user_data)
56 {
57 	BackupInfo *info = user_data;
58 
59 	if (info->callback) {
60 		info->callback (info->error, info->user_data);
61 	}
62 
63 	return FALSE;
64 }
65 
66 static void
backup_info_free(gpointer user_data)67 backup_info_free (gpointer user_data)
68 {
69 	BackupInfo *info = user_data;
70 
71 	if (info->destination) {
72 		g_object_unref (info->destination);
73 	}
74 
75 	if (info->file) {
76 		g_object_unref (info->file);
77 	}
78 
79 	if (info->destroy) {
80 		info->destroy (info->user_data);
81 	}
82 
83 	g_clear_error (&info->error);
84 
85 	g_slice_free (BackupInfo, info);
86 }
87 
88 static void
backup_job(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)89 backup_job (GTask        *task,
90             gpointer      source_object,
91             gpointer      task_data,
92             GCancellable *cancellable)
93 {
94 	BackupInfo *info = task_data;
95 
96 	GFile *parent_file, *temp_file;
97 	gchar *temp_path, *src_path;
98 
99 	sqlite3 *src_db = NULL;
100 	sqlite3 *temp_db = NULL;
101 	sqlite3_backup *backup = NULL;
102 
103 	src_path = g_file_get_path (info->file);
104 	parent_file = g_file_get_parent (info->destination);
105 	temp_file = g_file_get_child (parent_file, TRACKER_DB_BACKUP_META_FILENAME_T);
106 	g_file_delete (temp_file, NULL, NULL);
107 	temp_path = g_file_get_path (temp_file);
108 
109 	if (sqlite3_open_v2 (src_path, &src_db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
110 		g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
111 		             "Could not open sqlite3 database:'%s'", src_path);
112 	}
113 
114 	if (!info->error && sqlite3_open (temp_path, &temp_db) != SQLITE_OK) {
115 		g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
116 		             "Could not open sqlite3 database:'%s'", temp_path);
117 	}
118 
119 	if (!info->error) {
120 		backup = sqlite3_backup_init (temp_db, "main", src_db, "main");
121 
122 		if (!backup) {
123 			g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
124 				     "Unable to initialize sqlite3 backup from '%s' to '%s'", src_path, temp_path);
125 		}
126 	}
127 
128 	if (!info->error && sqlite3_backup_step (backup, -1) != SQLITE_DONE) {
129 		g_set_error (&info->error, TRACKER_DB_BACKUP_ERROR, TRACKER_DB_BACKUP_ERROR_UNKNOWN,
130 		             "Unable to complete sqlite3 backup");
131 	}
132 
133 	if (backup) {
134 		if (sqlite3_backup_finish (backup) != SQLITE_OK) {
135 			if (info->error) {
136 				/* sqlite3_backup_finish can provide more detailed error message */
137 				g_clear_error (&info->error);
138 			}
139 			g_set_error (&info->error,
140 			             TRACKER_DB_BACKUP_ERROR,
141 			             TRACKER_DB_BACKUP_ERROR_UNKNOWN,
142 				     "Unable to finish sqlite3 backup: %s",
143 				     sqlite3_errmsg (temp_db));
144 		}
145 		backup = NULL;
146 	}
147 
148 	if (temp_db) {
149 		sqlite3_close (temp_db);
150 		temp_db = NULL;
151 	}
152 
153 	if (src_db) {
154 		sqlite3_close (src_db);
155 		src_db = NULL;
156 	}
157 
158 	if (!info->error) {
159 		g_file_move (temp_file, info->destination,
160 		             G_FILE_COPY_OVERWRITE,
161 		             NULL, NULL, NULL,
162 		             &info->error);
163 	}
164 
165 	g_free (src_path);
166 	g_free (temp_path);
167 	g_object_unref (temp_file);
168 	g_object_unref (parent_file);
169 
170 	g_idle_add_full (G_PRIORITY_DEFAULT, perform_callback, info,
171 	                 backup_info_free);
172 }
173 
174 void
tracker_db_backup_save(GFile * destination,GFile * file,TrackerDBBackupFinished callback,gpointer user_data,GDestroyNotify destroy)175 tracker_db_backup_save (GFile                   *destination,
176                         GFile                   *file,
177                         TrackerDBBackupFinished  callback,
178                         gpointer                 user_data,
179                         GDestroyNotify           destroy)
180 {
181 	GTask *task;
182 	BackupInfo *info;
183 
184 	info = g_slice_new0 (BackupInfo);
185 
186 	info->destination = g_object_ref (destination);
187 	info->file = g_object_ref (file);
188 
189 	info->callback = callback;
190 	info->user_data = user_data;
191 	info->destroy = destroy;
192 
193 	task = g_task_new (NULL, NULL, NULL, NULL);
194 
195 	g_task_set_task_data (task, info, NULL);
196 	g_task_run_in_thread (task, backup_job);
197 	g_object_unref (task);
198 }
199 
200