tracker-0.16.2/src/tracker-writeback/tracker-writeback-file.c

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 }