No issues found
1 /*
2 * Copyright (C) 2009, 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 General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 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 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU 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 <stdio.h>
23 #include <fcntl.h> /* O_WRONLY */
24
25 #include <gio/gunixoutputstream.h>
26
27 #include <libtracker-common/tracker-file-utils.h>
28 #include <libtracker-common/tracker-common.h>
29
30 #include "tracker-writeback-file.h"
31
32 static gboolean tracker_writeback_file_update_metadata (TrackerWriteback *writeback,
33 GPtrArray *values,
34 TrackerSparqlConnection *connection,
35 GCancellable *cancellable,
36 GError **error);
37
38 G_DEFINE_ABSTRACT_TYPE (TrackerWritebackFile, tracker_writeback_file, TRACKER_TYPE_WRITEBACK)
39
40 static void
41 tracker_writeback_file_class_init (TrackerWritebackFileClass *klass)
42 {
43 TrackerWritebackClass *writeback_class = TRACKER_WRITEBACK_CLASS (klass);
44
45 writeback_class->update_metadata = tracker_writeback_file_update_metadata;
46 }
47
48 static void
49 tracker_writeback_file_init (TrackerWritebackFile *writeback_file)
50 {
51 }
52
53 static GFile *
54 create_temporary_file (GFile *file,
55 GFileInfo *file_info,
56 GError **in_error)
57 {
58 GInputStream *input_stream;
59 GOutputStream *output_stream;
60 GFile *tmp_file, *parent;
61 gchar *dir, *name, *tmp_path;
62 guint32 mode;
63 gint fd;
64 GError *error = NULL;
65
66 if (!g_file_is_native (file)) {
67 gchar *uri;
68
69 uri = g_file_get_uri (file);
70 g_warning ("Could not create temporary file, file is not native: '%s'", uri);
71 g_free (uri);
72
73 g_set_error (in_error,
74 G_IO_ERROR,
75 G_IO_ERROR_FAILED,
76 "Could not create temporary file, file is not native: '%s'",
77 uri);
78
79 return NULL;
80 }
81
82 /* Create input stream */
83 input_stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
84
85 if (error) {
86 g_critical ("Could not create temporary file, %s", error->message);
87 g_propagate_error (in_error, error);
88 return NULL;
89 }
90
91 /* Create output stream in a tmp file */
92 parent = g_file_get_parent (file);
93 dir = g_file_get_path (parent);
94 g_object_unref (parent);
95
96 name = g_file_get_basename (file);
97 tmp_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S ".tracker-XXXXXX.%s",
98 dir, name);
99 g_free (dir);
100 g_free (name);
101
102 mode = g_file_info_get_attribute_uint32 (file_info,
103 G_FILE_ATTRIBUTE_UNIX_MODE);
104 fd = g_mkstemp_full (tmp_path, O_WRONLY, mode);
105
106 output_stream = g_unix_output_stream_new (fd, TRUE);
107
108 /* Splice the original file into the tmp file */
109 g_output_stream_splice (output_stream,
110 input_stream,
111 G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
112 G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
113 NULL, &error);
114
115 g_object_unref (output_stream);
116 g_object_unref (input_stream);
117
118 tmp_file = g_file_new_for_path (tmp_path);
119 g_free (tmp_path);
120
121 if (error) {
122 g_critical ("Could not copy temporary file, %s", error->message);
123 g_propagate_error (in_error, error);
124 g_file_delete (tmp_file, NULL, NULL);
125 g_object_unref (tmp_file);
126
127 return NULL;
128 }
129
130 return tmp_file;
131 }
132
133 static gboolean
134 tracker_writeback_file_update_metadata (TrackerWriteback *writeback,
135 GPtrArray *values,
136 TrackerSparqlConnection *connection,
137 GCancellable *cancellable,
138 GError **error)
139 {
140 TrackerWritebackFileClass *writeback_file_class;
141 gboolean retval;
142 GFile *file, *tmp_file;
143 GFileInfo *file_info;
144 GStrv row;
145 const gchar * const *content_types;
146 const gchar *mime_type;
147 guint n;
148 GError *n_error = NULL;
149
150 writeback_file_class = TRACKER_WRITEBACK_FILE_GET_CLASS (writeback);
151
152 if (!writeback_file_class->update_file_metadata) {
153 g_critical ("%s doesn't implement update_file_metadata()",
154 G_OBJECT_TYPE_NAME (writeback));
155
156 g_set_error (error,
157 G_IO_ERROR,
158 G_IO_ERROR_FAILED,
159 "%s doesn't implement update_file_metadata()",
160 G_OBJECT_TYPE_NAME (writeback));
161
162 return FALSE;
163 }
164
165 if (!writeback_file_class->content_types) {
166 g_critical ("%s doesn't implement content_types()",
167 G_OBJECT_TYPE_NAME (writeback));
168
169 g_set_error (error,
170 G_IO_ERROR,
171 G_IO_ERROR_FAILED,
172 "%s doesn't implement content_types()",
173 G_OBJECT_TYPE_NAME (writeback));
174
175 return FALSE;
176 }
177
178 /* Get the file from the row */
179 row = g_ptr_array_index (values, 0);
180 file = g_file_new_for_uri (row[0]);
181
182 file_info = g_file_query_info (file,
183 G_FILE_ATTRIBUTE_UNIX_MODE ","
184 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
185 G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
186 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
187 NULL, NULL);
188
189 if (!file_info) {
190 g_object_unref (file);
191
192 g_set_error (error,
193 G_IO_ERROR,
194 G_IO_ERROR_FAILED,
195 "%s doesn't exist",
196 row[0]);
197
198 return FALSE;
199 }
200
201 if (!g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
202 g_object_unref (file_info);
203 g_object_unref (file);
204
205 g_set_error (error,
206 G_IO_ERROR,
207 G_IO_ERROR_FAILED,
208 "%s not writable",
209 row[0]);
210
211 return FALSE;
212 }
213
214 mime_type = g_file_info_get_content_type (file_info);
215 content_types = (writeback_file_class->content_types) (TRACKER_WRITEBACK_FILE (writeback));
216
217 retval = FALSE;
218
219 for (n = 0; content_types[n] != NULL; n++) {
220 if (g_strcmp0 (mime_type, content_types[n]) == 0) {
221 retval = TRUE;
222 break;
223 }
224 }
225
226 if (!retval) {
227 /* module does not support writeback for this file */
228 g_object_unref (file_info);
229 g_object_unref (file);
230
231 g_set_error_literal (error,
232 TRACKER_DBUS_ERROR,
233 TRACKER_DBUS_ERROR_UNSUPPORTED,
234 "Module does not support writeback for this file");
235
236 return FALSE;
237 }
238
239 /* Copy to a temporary file so we can perform an atomic write on move */
240 tmp_file = create_temporary_file (file, file_info, &n_error);
241
242 if (!tmp_file) {
243 g_object_unref (file);
244 g_propagate_error (error, n_error);
245 g_object_unref (file_info);
246 return FALSE;
247 }
248
249 retval = (writeback_file_class->update_file_metadata) (TRACKER_WRITEBACK_FILE (writeback),
250 tmp_file,
251 values,
252 connection,
253 cancellable,
254 &n_error);
255
256 if (!retval) {
257 /* Delete the temporary file and preserve original */
258 g_file_delete (tmp_file, NULL, NULL);
259 } else {
260 GError *m_error = NULL;
261 /* Move back the modified file to the original location */
262 g_file_move (tmp_file, file,
263 G_FILE_COPY_OVERWRITE,
264 NULL, NULL, NULL, NULL);
265 /* Set file attributes on tmp_file using file_info of original file */
266 g_file_set_attributes_from_info (tmp_file, file_info, 0, NULL, &m_error);
267 if (m_error) {
268 g_warning ("Can't restore permissions of original file for %s",
269 row[0]);
270 g_error_free (m_error);
271 }
272 }
273
274 g_object_unref (file_info);
275 g_object_unref (tmp_file);
276 g_object_unref (file);
277
278 if (n_error) {
279 g_propagate_error (error, n_error);
280 }
281
282 return retval;
283 }