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 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-miners.h"
21 
22 #include <sys/statvfs.h>
23 #include <fcntl.h>
24 #ifdef __linux__
25 #include <sys/ioctl.h>
26 #include <linux/msdos_fs.h>
27 #endif /* __linux__ */
28 #include <unistd.h>
29 
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32 
33 #include <gio/gio.h>
34 #include <gio/gunixfdlist.h>
35 #include <gio/gunixinputstream.h>
36 
37 #include <libtracker-miners-common/tracker-common.h>
38 #include <libtracker-sparql/tracker-ontologies.h>
39 #include <libtracker-extract/tracker-extract.h>
40 
41 #include "tracker-power.h"
42 #include "tracker-miner-files.h"
43 #include "tracker-config.h"
44 #include "tracker-storage.h"
45 #include "tracker-extract-watchdog.h"
46 #include "tracker-thumbnailer.h"
47 
48 #define DISK_SPACE_CHECK_FREQUENCY 10
49 #define SECONDS_PER_DAY 86400
50 
51 /* Stamp files to know crawling/indexing state */
52 #define FIRST_INDEX_FILENAME          "first-index.txt"
53 #define LAST_CRAWL_FILENAME           "last-crawl.txt"
54 #define NEED_MTIME_CHECK_FILENAME     "no-need-mtime-check.txt"
55 
56 #define TRACKER_EXTRACT_DATA_SOURCE TRACKER_PREFIX_TRACKER "extractor-data-source"
57 
58 #define TRACKER_MINER_FILES_GET_PRIVATE(o) (tracker_miner_files_get_instance_private (TRACKER_MINER_FILES (o)))
59 
60 static GQuark miner_files_error_quark = 0;
61 
62 typedef struct ProcessFileData ProcessFileData;
63 
64 struct ProcessFileData {
65 	TrackerMinerFiles *miner;
66 	GCancellable *cancellable;
67 	GFile *file;
68 	gchar *mime_type;
69 	GTask *task;
70 };
71 
72 struct TrackerMinerFilesPrivate {
73 	TrackerConfig *config;
74 	TrackerStorage *storage;
75 
76 	TrackerExtractWatchdog *extract_watchdog;
77 	gboolean checking_unextracted;
78 	guint grace_period_timeout_id;
79 	GCancellable *extract_check_cancellable;
80 	gchar *extract_check_query;
81 
82 	GVolumeMonitor *volume_monitor;
83 
84 	GSList *index_recursive_directories;
85 	GSList *index_single_directories;
86 
87 	guint disk_space_check_id;
88 	gboolean disk_space_pause;
89 
90 	gboolean low_battery_pause;
91 
92 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
93 	TrackerPower *power;
94 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
95 	gulong finished_handler;
96 
97 	GDBusConnection *connection;
98 
99 	GQuark quark_mount_point_uuid;
100 
101 	guint force_recheck_id;
102 
103 	gboolean mtime_check;
104 	gboolean index_removable_devices;
105 	gboolean index_optical_discs;
106 	guint volumes_changed_id;
107 
108 	gboolean mount_points_initialized;
109 
110 	guint stale_volumes_check_id;
111 
112 	GList *extraction_queue;
113 
114 	TrackerThumbnailer *thumbnailer;
115 
116 	GHashTable *writeback_tasks;
117 	gboolean paused_for_writeback;
118 };
119 
120 typedef struct {
121 	GMainLoop *main_loop;
122 	TrackerMiner *miner;
123 } ThumbnailMoveData;
124 
125 enum {
126 	VOLUME_MOUNTED_IN_STORE = 1 << 0,
127 	VOLUME_MOUNTED = 1 << 1
128 };
129 
130 enum {
131 	PROP_0,
132 	PROP_CONFIG
133 };
134 
135 enum {
136 	WRITEBACK,
137 	N_SIGNALS
138 };
139 
140 static guint signals[N_SIGNALS] = { 0 };
141 
142 static void        miner_files_set_property             (GObject              *object,
143                                                          guint                 param_id,
144                                                          const GValue         *value,
145                                                          GParamSpec           *pspec);
146 static void        miner_files_get_property             (GObject              *object,
147                                                          guint                 param_id,
148                                                          GValue               *value,
149                                                          GParamSpec           *pspec);
150 static void        miner_files_finalize                 (GObject              *object);
151 static void        miner_files_initable_iface_init      (GInitableIface       *iface);
152 static gboolean    miner_files_initable_init            (GInitable            *initable,
153                                                          GCancellable         *cancellable,
154                                                          GError              **error);
155 static void        mount_pre_unmount_cb                 (GVolumeMonitor       *volume_monitor,
156                                                          GMount               *mount,
157                                                          TrackerMinerFiles    *mf);
158 
159 static void        mount_point_added_cb                 (TrackerStorage       *storage,
160                                                          const gchar          *uuid,
161                                                          const gchar          *mount_point,
162                                                          const gchar          *mount_name,
163                                                          gboolean              removable,
164                                                          gboolean              optical,
165                                                          gpointer              user_data);
166 static void        mount_point_removed_cb               (TrackerStorage       *storage,
167                                                          const gchar          *uuid,
168                                                          const gchar          *mount_point,
169                                                          gpointer              user_data);
170 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
171 static void        check_battery_status                 (TrackerMinerFiles    *fs);
172 static void        battery_status_cb                    (GObject              *object,
173                                                          GParamSpec           *pspec,
174                                                          gpointer              user_data);
175 static void        index_on_battery_cb                  (GObject    *object,
176                                                          GParamSpec *pspec,
177                                                          gpointer    user_data);
178 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
179 static void        init_mount_points                    (TrackerMinerFiles    *miner);
180 static void        init_stale_volume_removal            (TrackerMinerFiles    *miner);
181 static void        disk_space_check_start               (TrackerMinerFiles    *mf);
182 static void        disk_space_check_stop                (TrackerMinerFiles    *mf);
183 static void        low_disk_space_limit_cb              (GObject              *gobject,
184                                                          GParamSpec           *arg1,
185                                                          gpointer              user_data);
186 static void        index_recursive_directories_cb       (GObject              *gobject,
187                                                          GParamSpec           *arg1,
188                                                          gpointer              user_data);
189 static void        index_single_directories_cb          (GObject              *gobject,
190                                                          GParamSpec           *arg1,
191                                                          gpointer              user_data);
192 static gboolean    miner_files_force_recheck_idle       (gpointer user_data);
193 static void        trigger_recheck_cb                   (GObject              *gobject,
194                                                          GParamSpec           *arg1,
195                                                          gpointer              user_data);
196 static void        index_volumes_changed_cb             (GObject              *gobject,
197                                                          GParamSpec           *arg1,
198                                                          gpointer              user_data);
199 static gboolean    miner_files_process_file             (TrackerMinerFS       *fs,
200                                                          GFile                *file,
201                                                          GTask                *task);
202 static gboolean    miner_files_process_file_attributes  (TrackerMinerFS       *fs,
203                                                          GFile                *file,
204                                                          GTask                *task);
205 static gchar *     miner_files_remove_children          (TrackerMinerFS       *fs,
206                                                          GFile                *file);
207 static gchar *     miner_files_remove_file              (TrackerMinerFS       *fs,
208                                                          GFile                *file);
209 static gchar *     miner_files_move_file                (TrackerMinerFS       *fs,
210                                                          GFile                *file,
211                                                          GFile                *source_file,
212                                                          gboolean              recursive);
213 static void        miner_files_finished                 (TrackerMinerFS       *fs,
214                                                          gdouble               elapsed,
215                                                          gint                  directories_found,
216                                                          gint                  directories_ignored,
217                                                          gint                  files_found,
218                                                          gint                  files_ignored);
219 static void        miner_finished_cb                    (TrackerMinerFS *fs,
220                                                          gdouble         seconds_elapsed,
221                                                          guint           total_directories_found,
222                                                          guint           total_directories_ignored,
223                                                          guint           total_files_found,
224                                                          guint           total_files_ignored,
225                                                          gpointer        user_data);
226 
227 static gboolean    miner_files_in_removable_media_remove_by_type  (TrackerMinerFiles  *miner,
228                                                                    TrackerStorageType  type);
229 static void        miner_files_in_removable_media_remove_by_date  (TrackerMinerFiles  *miner,
230                                                                    const gchar        *date);
231 
232 static void        miner_files_add_removable_or_optical_directory (TrackerMinerFiles *mf,
233                                                                    const gchar       *mount_path,
234                                                                    const gchar       *uuid);
235 
236 static void        miner_files_update_filters                     (TrackerMinerFiles *files);
237 
238 
239 static GInitableIface* miner_files_initable_parent_iface;
240 
241 G_DEFINE_TYPE_WITH_CODE (TrackerMinerFiles, tracker_miner_files, TRACKER_TYPE_MINER_FS,
242                          G_ADD_PRIVATE (TrackerMinerFiles)
243                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
244                                                 miner_files_initable_iface_init));
245 
246 static void
sync_writeback_pause_state(TrackerMinerFiles * mf)247 sync_writeback_pause_state (TrackerMinerFiles *mf)
248 {
249 	guint n_writeback_tasks = g_hash_table_size (mf->private->writeback_tasks);
250 
251 	if (n_writeback_tasks > 0 && !mf->private->paused_for_writeback) {
252 		tracker_miner_pause (TRACKER_MINER (mf));
253 		mf->private->paused_for_writeback = TRUE;
254 	} else if (n_writeback_tasks == 0 && mf->private->paused_for_writeback) {
255 		mf->private->paused_for_writeback = FALSE;
256 		tracker_miner_resume (TRACKER_MINER (mf));
257 	}
258 }
259 
260 static void
writeback_remove_recursively(TrackerMinerFiles * mf,GFile * file)261 writeback_remove_recursively (TrackerMinerFiles *mf,
262 			      GFile             *file)
263 {
264 	GHashTableIter iter;
265 	GFile *writeback_file;
266 
267 	if (g_hash_table_size (mf->private->writeback_tasks) == 0)
268 		return;
269 
270 	/* Remove and cancel writeback tasks in this directory */
271 	g_hash_table_iter_init (&iter, mf->private->writeback_tasks);
272 	while (g_hash_table_iter_next (&iter, (gpointer*) &writeback_file, NULL)) {
273 		if (g_file_equal (writeback_file, file) ||
274 		    g_file_has_prefix (writeback_file, file)) {
275 			g_hash_table_iter_remove (&iter);
276 		}
277 	}
278 
279 	sync_writeback_pause_state (mf);
280 }
281 
282 static gboolean
miner_files_filter_event(TrackerMinerFS * fs,TrackerMinerFSEventType type,GFile * file,GFile * source_file)283 miner_files_filter_event (TrackerMinerFS          *fs,
284                           TrackerMinerFSEventType  type,
285                           GFile                   *file,
286                           GFile                   *source_file)
287 {
288 	TrackerMinerFiles *mf = TRACKER_MINER_FILES (fs);
289 
290 	switch (type) {
291 	case TRACKER_MINER_FS_EVENT_CREATED:
292 	case TRACKER_MINER_FS_EVENT_UPDATED:
293 		break;
294 	case TRACKER_MINER_FS_EVENT_DELETED:
295 		writeback_remove_recursively (mf, file);
296 		break;
297 	case TRACKER_MINER_FS_EVENT_MOVED:
298 		/* If the origin file is also being written back,
299 		 * cancel it as this is an external operation.
300 		 */
301 		writeback_remove_recursively (mf, source_file);
302 		break;
303 	}
304 
305 	return FALSE;
306 }
307 
308 static void
tracker_miner_files_class_init(TrackerMinerFilesClass * klass)309 tracker_miner_files_class_init (TrackerMinerFilesClass *klass)
310 {
311 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
312 	TrackerMinerFSClass *miner_fs_class = TRACKER_MINER_FS_CLASS (klass);
313 
314 	object_class->finalize = miner_files_finalize;
315 	object_class->get_property = miner_files_get_property;
316 	object_class->set_property = miner_files_set_property;
317 
318 	miner_fs_class->process_file = miner_files_process_file;
319 	miner_fs_class->process_file_attributes = miner_files_process_file_attributes;
320 	miner_fs_class->finished = miner_files_finished;
321 	miner_fs_class->remove_file = miner_files_remove_file;
322 	miner_fs_class->remove_children = miner_files_remove_children;
323 	miner_fs_class->move_file = miner_files_move_file;
324 	miner_fs_class->filter_event = miner_files_filter_event;
325 
326 	/**
327 	 * TrackerMinerFiles::writeback-file:
328 	 * @miner: the #TrackerMinerFiles
329 	 * @file: a #GFile
330 	 * @rdf_types: the set of RDF types
331 	 * @results: (element-type GStrv): a set of results prepared by the preparation query
332 	 * @cancellable: a #GCancellable
333 	 *
334 	 * The ::writeback-file signal is emitted whenever a file must be written
335 	 * back
336 	 *
337 	 * Returns: %TRUE on success, %FALSE otherwise
338 	 **/
339 	signals[WRITEBACK] =
340 		g_signal_new ("writeback",
341 		              G_OBJECT_CLASS_TYPE (object_class),
342 		              G_SIGNAL_RUN_LAST,
343 		              0, NULL,
344 		              NULL,
345 		              NULL,
346 		              G_TYPE_NONE,
347 		              4,
348 		              G_TYPE_FILE,
349 		              G_TYPE_STRV,
350 		              G_TYPE_PTR_ARRAY,
351 		              G_TYPE_CANCELLABLE);
352 
353 	g_object_class_install_property (object_class,
354 	                                 PROP_CONFIG,
355 	                                 g_param_spec_object ("config",
356 	                                                      "Config",
357 	                                                      "Config",
358 	                                                      TRACKER_TYPE_CONFIG,
359 	                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
360 
361 	miner_files_error_quark = g_quark_from_static_string ("TrackerMinerFiles");
362 }
363 
364 static void
check_unextracted_cb(GObject * object,GAsyncResult * res,gpointer user_data)365 check_unextracted_cb (GObject      *object,
366                       GAsyncResult *res,
367                       gpointer      user_data)
368 {
369 	TrackerMinerFiles *mf = user_data;
370 	TrackerExtractWatchdog *watchdog = mf->private->extract_watchdog;
371 	TrackerSparqlCursor *cursor;
372 	GError *error = NULL;
373 
374 	mf->private->checking_unextracted = FALSE;
375 	cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object),
376 	                                                 res, &error);
377 	if (error) {
378 		g_warning ("Could not check unextracted items: %s", error->message);
379 		g_error_free (error);
380 		return;
381 	}
382 
383 	if (tracker_sparql_cursor_next (cursor, mf->private->extract_check_cancellable, NULL))
384 		tracker_extract_watchdog_ensure_started (watchdog);
385 	else
386 		g_debug ("Not starting extractor. Nothing to do.");
387 
388 	g_object_unref (cursor);
389 }
390 
391 static void
tracker_miner_files_check_unextracted(TrackerMinerFiles * mf)392 tracker_miner_files_check_unextracted (TrackerMinerFiles *mf)
393 {
394 	if (mf->private->checking_unextracted)
395 		return;
396 
397 	mf->private->checking_unextracted = TRUE;
398 	tracker_sparql_connection_query_async (tracker_miner_get_connection (TRACKER_MINER (mf)),
399 					       mf->private->extract_check_query,
400 	                                       mf->private->extract_check_cancellable,
401 	                                       check_unextracted_cb, mf);
402 }
403 
404 static void
cancel_and_unref(gpointer data)405 cancel_and_unref (gpointer data)
406 {
407 	GCancellable *cancellable = data;
408 
409 	if (cancellable) {
410 		g_cancellable_cancel (cancellable);
411 		g_object_unref (cancellable);
412 	}
413 }
414 
415 static gboolean
extractor_lost_timeout_cb(gpointer user_data)416 extractor_lost_timeout_cb (gpointer user_data)
417 {
418 	TrackerMinerFiles *mf = user_data;
419 
420 	tracker_miner_files_check_unextracted (mf);
421 	mf->private->grace_period_timeout_id = 0;
422 	return G_SOURCE_REMOVE;
423 }
424 
425 
426 static void
on_extractor_lost(TrackerExtractWatchdog * watchdog,TrackerMinerFiles * mf)427 on_extractor_lost (TrackerExtractWatchdog *watchdog,
428                    TrackerMinerFiles      *mf)
429 {
430 	g_debug ("tracker-extract vanished, maybe restarting.");
431 
432 	/* Give a period of grace before restarting, so we allow replacing
433 	 * from eg. a terminal.
434 	 */
435 	mf->private->grace_period_timeout_id =
436 		g_timeout_add_seconds (1, extractor_lost_timeout_cb, mf);
437 }
438 
439 static void
tracker_miner_files_init(TrackerMinerFiles * mf)440 tracker_miner_files_init (TrackerMinerFiles *mf)
441 {
442 	TrackerMinerFilesPrivate *priv;
443 	gchar *rdf_types_str;
444 	GStrv rdf_types;
445 
446 	priv = mf->private = TRACKER_MINER_FILES_GET_PRIVATE (mf);
447 
448 	priv->storage = tracker_storage_new ();
449 
450 	g_signal_connect (priv->storage, "mount-point-added",
451 	                  G_CALLBACK (mount_point_added_cb),
452 	                  mf);
453 
454 	g_signal_connect (priv->storage, "mount-point-removed",
455 	                  G_CALLBACK (mount_point_removed_cb),
456 	                  mf);
457 
458 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
459 	priv->power = tracker_power_new ();
460 
461 	if (priv->power) {
462 		g_signal_connect (priv->power, "notify::on-low-battery",
463 		                  G_CALLBACK (battery_status_cb),
464 		                  mf);
465 		g_signal_connect (priv->power, "notify::on-battery",
466 		                  G_CALLBACK (battery_status_cb),
467 		                  mf);
468 	}
469 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
470 
471 	priv->finished_handler = g_signal_connect_after (mf, "finished",
472 	                                                 G_CALLBACK (miner_finished_cb),
473 	                                                 NULL);
474 
475 	priv->volume_monitor = g_volume_monitor_get ();
476 	g_signal_connect (priv->volume_monitor, "mount-pre-unmount",
477 	                  G_CALLBACK (mount_pre_unmount_cb),
478 	                  mf);
479 
480 	priv->mtime_check = TRUE;
481 	priv->quark_mount_point_uuid = g_quark_from_static_string ("tracker-mount-point-uuid");
482 
483 	priv->writeback_tasks = g_hash_table_new_full (g_file_hash,
484 	                                               (GEqualFunc) g_file_equal,
485 	                                               g_object_unref, cancel_and_unref);
486 
487 	priv->extract_check_cancellable = g_cancellable_new ();
488 
489 	rdf_types = tracker_extract_module_manager_get_rdf_types ();
490 	rdf_types_str = g_strjoinv (",", rdf_types);
491 	g_strfreev (rdf_types);
492 
493 	priv->extract_check_query = g_strdup_printf ("SELECT ?u { "
494 	                                             "  GRAPH <" TRACKER_OWN_GRAPH_URN "> {"
495 						     "    ?u a nfo:FileDataObject ;"
496 						     "       tracker:available true ; "
497 						     "       a ?class . "
498 						     "    FILTER (?class IN (%s) && "
499 						     "            NOT EXISTS { ?u nie:dataSource <" TRACKER_EXTRACT_DATA_SOURCE "> })"
500 	                                             "  }"
501 						     "} LIMIT 1",
502 						     rdf_types_str);
503 	g_free (rdf_types_str);
504 }
505 
506 static void
miner_files_initable_iface_init(GInitableIface * iface)507 miner_files_initable_iface_init (GInitableIface *iface)
508 {
509 	miner_files_initable_parent_iface = g_type_interface_peek_parent (iface);
510 	iface->init = miner_files_initable_init;
511 }
512 
513 static void
miner_files_add_application_dir(TrackerMinerFiles * mf,const gchar * dir)514 miner_files_add_application_dir (TrackerMinerFiles *mf,
515                                  const gchar       *dir)
516 {
517 	TrackerIndexingTree *indexing_tree;
518 	GFile *file;
519 	gchar *path;
520 
521 	indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
522 
523 	/* Add $dir/applications */
524 	path = g_build_filename (dir, "applications", NULL);
525 	file = g_file_new_for_path (path);
526 	g_message ("  Adding:'%s'", path);
527 
528 	tracker_indexing_tree_add (indexing_tree, file,
529 				   TRACKER_DIRECTORY_FLAG_RECURSE |
530 				   TRACKER_DIRECTORY_FLAG_MONITOR |
531 				   TRACKER_DIRECTORY_FLAG_CHECK_MTIME);
532 	g_object_unref (file);
533 	g_free (path);
534 }
535 
536 static gboolean
miner_files_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)537 miner_files_initable_init (GInitable     *initable,
538                            GCancellable  *cancellable,
539                            GError       **error)
540 {
541 	TrackerMinerFiles *mf;
542 	TrackerMinerFS *fs;
543 	TrackerIndexingTree *indexing_tree;
544 	TrackerDirectoryFlags flags;
545 	const gchar *user_data_dir;
546 	const gchar * const *xdg_dirs;
547 	GError *inner_error = NULL;
548 	GSList *mounts = NULL;
549 	GSList *dirs;
550 	GSList *m;
551 	gint i;
552 
553 	/* Chain up parent's initable callback before calling child's one */
554 	if (!miner_files_initable_parent_iface->init (initable, cancellable, &inner_error)) {
555 		g_propagate_error (error, inner_error);
556 		return FALSE;
557 	}
558 
559 	mf = TRACKER_MINER_FILES (initable);
560 	fs = TRACKER_MINER_FS (initable);
561 	indexing_tree = tracker_miner_fs_get_indexing_tree (fs);
562 	tracker_indexing_tree_set_filter_hidden (indexing_tree, TRUE);
563 	g_signal_connect_swapped (indexing_tree, "directory-removed",
564 				  G_CALLBACK (writeback_remove_recursively), mf);
565 
566 	miner_files_update_filters (mf);
567 
568 	/* Set up extractor and signals */
569 	mf->private->connection =  g_bus_get_sync (TRACKER_IPC_BUS, NULL, &inner_error);
570 	if (!mf->private->connection) {
571 		g_propagate_error (error, inner_error);
572 		g_prefix_error (error,
573 		                "Could not connect to the D-Bus session bus. ");
574 		return FALSE;
575 	}
576 
577 	/* We must have a configuration setup here */
578 	if (G_UNLIKELY (!mf->private->config)) {
579 		g_set_error (error,
580 		             TRACKER_MINER_ERROR,
581 		             0,
582 		             "No config set for miner %s",
583 		             G_OBJECT_TYPE_NAME (mf));
584 		return FALSE;
585 	}
586 
587 	/* Setup mount points, we MUST have config set up before we
588 	 * init mount points because the config is used in that
589 	 * function.
590 	 */
591 	mf->private->index_removable_devices = tracker_config_get_index_removable_devices (mf->private->config);
592 
593 	/* Note that if removable devices not indexed, optical discs
594 	 * will also never be indexed */
595 	mf->private->index_optical_discs = (mf->private->index_removable_devices ?
596 	                                    tracker_config_get_index_optical_discs (mf->private->config) :
597 	                                    FALSE);
598 
599 	init_mount_points (mf);
600 
601 	/* If this happened AFTER we have initialized mount points, initialize
602 	 * stale volume removal now. */
603 	if (mf->private->mount_points_initialized) {
604 		init_stale_volume_removal (mf);
605 	}
606 
607 	if (mf->private->index_removable_devices) {
608 		/* Get list of roots for removable devices (excluding optical) */
609 		mounts = tracker_storage_get_device_roots (mf->private->storage,
610 		                                           TRACKER_STORAGE_REMOVABLE,
611 		                                           TRUE);
612 	}
613 
614 	if (mf->private->index_optical_discs) {
615 		/* Get list of roots for removable+optical devices */
616 		m = tracker_storage_get_device_roots (mf->private->storage,
617 		                                      TRACKER_STORAGE_OPTICAL | TRACKER_STORAGE_REMOVABLE,
618 		                                      TRUE);
619 		mounts = g_slist_concat (mounts, m);
620 	}
621 
622 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
623 	check_battery_status (mf);
624 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
625 
626 	g_message ("Setting up directories to iterate from config (IndexSingleDirectory)");
627 
628 	/* Fill in directories to inspect */
629 	dirs = tracker_config_get_index_single_directories (mf->private->config);
630 
631 	/* Copy in case of config changes */
632 	mf->private->index_single_directories = tracker_gslist_copy_with_string_data (dirs);
633 
634 	for (; dirs; dirs = dirs->next) {
635 		GFile *file;
636 
637 		/* Do some simple checks for silly locations */
638 		if (strcmp (dirs->data, "/dev") == 0 ||
639 		    strcmp (dirs->data, "/lib") == 0 ||
640 		    strcmp (dirs->data, "/proc") == 0 ||
641 		    strcmp (dirs->data, "/sys") == 0) {
642 			continue;
643 		}
644 
645 		if (g_str_has_prefix (dirs->data, g_get_tmp_dir ())) {
646 			continue;
647 		}
648 
649 		/* Make sure we don't crawl volumes. */
650 		if (mounts) {
651 			gboolean found = FALSE;
652 
653 			for (m = mounts; m && !found; m = m->next) {
654 				found = strcmp (m->data, dirs->data) == 0;
655 			}
656 
657 			if (found) {
658 				g_message ("  Duplicate found:'%s' - same as removable device path",
659 				           (gchar*) dirs->data);
660 				continue;
661 			}
662 		}
663 
664 		g_message ("  Adding:'%s'", (gchar*) dirs->data);
665 
666 		file = g_file_new_for_path (dirs->data);
667 
668 		flags = TRACKER_DIRECTORY_FLAG_NONE;
669 
670 		if (tracker_config_get_enable_monitors (mf->private->config)) {
671 			flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
672 		}
673 
674 		if (mf->private->mtime_check) {
675 			flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
676 		}
677 
678 		tracker_indexing_tree_add (indexing_tree, file, flags);
679 		g_object_unref (file);
680 	}
681 
682 	g_message ("Setting up directories to iterate from config (IndexRecursiveDirectory)");
683 
684 	dirs = tracker_config_get_index_recursive_directories (mf->private->config);
685 
686 	/* Copy in case of config changes */
687 	mf->private->index_recursive_directories = tracker_gslist_copy_with_string_data (dirs);
688 
689 	for (; dirs; dirs = dirs->next) {
690 		GFile *file;
691 
692 		/* Do some simple checks for silly locations */
693 		if (strcmp (dirs->data, "/dev") == 0 ||
694 		    strcmp (dirs->data, "/lib") == 0 ||
695 		    strcmp (dirs->data, "/proc") == 0 ||
696 		    strcmp (dirs->data, "/sys") == 0) {
697 			continue;
698 		}
699 
700 		if (g_str_has_prefix (dirs->data, g_get_tmp_dir ())) {
701 			continue;
702 		}
703 
704 		/* Make sure we don't crawl volumes. */
705 		if (mounts) {
706 			gboolean found = FALSE;
707 
708 			for (m = mounts; m && !found; m = m->next) {
709 				found = strcmp (m->data, dirs->data) == 0;
710 			}
711 
712 			if (found) {
713 				g_message ("  Duplicate found:'%s' - same as removable device path",
714 				           (gchar*) dirs->data);
715 				continue;
716 			}
717 		}
718 
719 		g_message ("  Adding:'%s'", (gchar*) dirs->data);
720 
721 		file = g_file_new_for_path (dirs->data);
722 
723 		flags = TRACKER_DIRECTORY_FLAG_RECURSE;
724 
725 		if (tracker_config_get_enable_monitors (mf->private->config)) {
726 			flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
727 		}
728 
729 		if (mf->private->mtime_check) {
730 			flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
731 		}
732 
733 		tracker_indexing_tree_add (indexing_tree, file, flags);
734 		g_object_unref (file);
735 	}
736 
737 	/* Add mounts */
738 	g_message ("Setting up directories to iterate from devices/discs");
739 
740 	if (!mf->private->index_removable_devices) {
741 		g_message ("  Removable devices are disabled in the config");
742 
743 		/* Make sure we don't have any resource in a volume of the given type */
744 		miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE);
745 	}
746 
747 	if (!mf->private->index_optical_discs) {
748 		g_message ("  Optical discs are disabled in the config");
749 
750 		/* Make sure we don't have any resource in a volume of the given type */
751 		miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE | TRACKER_STORAGE_OPTICAL);
752 	}
753 
754 	for (m = mounts; m; m = m->next) {
755 		miner_files_add_removable_or_optical_directory (mf,
756 		                                                (gchar *) m->data,
757 		                                                NULL);
758 	}
759 
760 	/* Add application directories */
761 	g_message ("Setting up applications to iterate from XDG system directories");
762 	xdg_dirs = g_get_system_data_dirs ();
763 
764 	for (i = 0; xdg_dirs[i]; i++) {
765 		miner_files_add_application_dir (mf, xdg_dirs[i]);
766 	}
767 
768 	user_data_dir = g_get_user_data_dir ();
769 	if (user_data_dir) {
770 		miner_files_add_application_dir (mf, user_data_dir);
771 	}
772 
773 	/* We want to get notified when config changes */
774 
775 	g_signal_connect (mf->private->config, "notify::low-disk-space-limit",
776 	                  G_CALLBACK (low_disk_space_limit_cb),
777 	                  mf);
778 	g_signal_connect (mf->private->config, "notify::index-recursive-directories",
779 	                  G_CALLBACK (index_recursive_directories_cb),
780 	                  mf);
781 	g_signal_connect (mf->private->config, "notify::index-single-directories",
782 	                  G_CALLBACK (index_single_directories_cb),
783 	                  mf);
784 	g_signal_connect (mf->private->config, "notify::ignored-directories",
785 	                  G_CALLBACK (trigger_recheck_cb),
786 	                  mf);
787 	g_signal_connect (mf->private->config, "notify::ignored-directories-with-content",
788 	                  G_CALLBACK (trigger_recheck_cb),
789 	                  mf);
790 	g_signal_connect (mf->private->config, "notify::ignored-files",
791 	                  G_CALLBACK (trigger_recheck_cb),
792 	                  mf);
793 	g_signal_connect (mf->private->config, "notify::enable-monitors",
794 	                  G_CALLBACK (trigger_recheck_cb),
795 	                  mf);
796 	g_signal_connect (mf->private->config, "notify::index-removable-devices",
797 	                  G_CALLBACK (index_volumes_changed_cb),
798 	                  mf);
799 	g_signal_connect (mf->private->config, "notify::index-optical-discs",
800 	                  G_CALLBACK (index_volumes_changed_cb),
801 	                  mf);
802 	g_signal_connect (mf->private->config, "notify::removable-days-threshold",
803 	                  G_CALLBACK (index_volumes_changed_cb),
804 	                  mf);
805 
806 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
807 
808 	g_signal_connect (mf->private->config, "notify::index-on-battery",
809 	                  G_CALLBACK (index_on_battery_cb),
810 	                  mf);
811 	g_signal_connect (mf->private->config, "notify::index-on-battery-first-time",
812 	                  G_CALLBACK (index_on_battery_cb),
813 	                  mf);
814 
815 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
816 
817 	g_slist_foreach (mounts, (GFunc) g_free, NULL);
818 	g_slist_free (mounts);
819 
820 	disk_space_check_start (mf);
821 
822 	mf->private->extract_watchdog = tracker_extract_watchdog_new ();
823 	g_signal_connect (mf->private->extract_watchdog, "lost",
824 	                  G_CALLBACK (on_extractor_lost), mf);
825 
826 	mf->private->thumbnailer = tracker_thumbnailer_new ();
827 
828 	return TRUE;
829 }
830 
831 static void
miner_files_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)832 miner_files_set_property (GObject      *object,
833                           guint         prop_id,
834                           const GValue *value,
835                           GParamSpec   *pspec)
836 {
837 	TrackerMinerFilesPrivate *priv;
838 
839 	priv = TRACKER_MINER_FILES_GET_PRIVATE (object);
840 
841 	switch (prop_id) {
842 	case PROP_CONFIG:
843 		priv->config = g_value_dup_object (value);
844 		break;
845 	default:
846 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
847 		break;
848 	}
849 }
850 
851 static void
miner_files_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)852 miner_files_get_property (GObject    *object,
853                           guint       prop_id,
854                           GValue     *value,
855                           GParamSpec *pspec)
856 {
857 	TrackerMinerFilesPrivate *priv;
858 
859 	priv = TRACKER_MINER_FILES_GET_PRIVATE (object);
860 
861 	switch (prop_id) {
862 	case PROP_CONFIG:
863 		g_value_set_object (value, priv->config);
864 		break;
865 	default:
866 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
867 		break;
868 	}
869 }
870 
871 static void
miner_files_finalize(GObject * object)872 miner_files_finalize (GObject *object)
873 {
874 	TrackerMinerFiles *mf;
875 	TrackerMinerFilesPrivate *priv;
876 
877 	mf = TRACKER_MINER_FILES (object);
878 	priv = mf->private;
879 
880 	g_cancellable_cancel (priv->extract_check_cancellable);
881 	g_object_unref (priv->extract_check_cancellable);
882 	g_free (priv->extract_check_query);
883 
884 	if (priv->grace_period_timeout_id != 0) {
885 		g_source_remove (priv->grace_period_timeout_id);
886 		priv->grace_period_timeout_id = 0;
887 	}
888 
889 	if (priv->extract_watchdog) {
890 		g_signal_handlers_disconnect_by_func (priv->extract_watchdog,
891 		                                      on_extractor_lost,
892 		                                      NULL);
893 		g_clear_object (&priv->extract_watchdog);
894 	}
895 
896 	if (priv->config) {
897 		g_signal_handlers_disconnect_by_func (priv->config,
898 		                                      low_disk_space_limit_cb,
899 		                                      NULL);
900 		g_object_unref (priv->config);
901 	}
902 
903 	disk_space_check_stop (TRACKER_MINER_FILES (object));
904 
905 	if (priv->index_recursive_directories) {
906 		g_slist_foreach (priv->index_recursive_directories, (GFunc) g_free, NULL);
907 		g_slist_free (priv->index_recursive_directories);
908 	}
909 
910 	if (priv->index_single_directories) {
911 		g_slist_foreach (priv->index_single_directories, (GFunc) g_free, NULL);
912 		g_slist_free (priv->index_single_directories);
913 	}
914 
915 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
916 	if (priv->power) {
917 		g_object_unref (priv->power);
918 	}
919 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
920 
921 	if (priv->storage) {
922 		g_object_unref (priv->storage);
923 	}
924 
925 	if (priv->volume_monitor) {
926 		g_signal_handlers_disconnect_by_func (priv->volume_monitor,
927 		                                      mount_pre_unmount_cb,
928 		                                      object);
929 		g_object_unref (priv->volume_monitor);
930 	}
931 
932 	if (priv->force_recheck_id) {
933 		g_source_remove (priv->force_recheck_id);
934 		priv->force_recheck_id = 0;
935 	}
936 
937 	if (priv->stale_volumes_check_id) {
938 		g_source_remove (priv->stale_volumes_check_id);
939 		priv->stale_volumes_check_id = 0;
940 	}
941 
942 	if (priv->thumbnailer) {
943 		g_object_unref (priv->thumbnailer);
944 	}
945 
946 	g_list_free (priv->extraction_queue);
947 	g_hash_table_destroy (priv->writeback_tasks);
948 
949 	G_OBJECT_CLASS (tracker_miner_files_parent_class)->finalize (object);
950 }
951 
952 static void
ensure_mount_point_exists(TrackerMinerFiles * miner,GFile * mount_point,GString * accumulator)953 ensure_mount_point_exists (TrackerMinerFiles *miner,
954                            GFile             *mount_point,
955                            GString           *accumulator)
956 {
957 	gchar *iri;
958 	gchar *uri;
959 
960 	uri = g_file_get_uri (mount_point);
961 
962 	/* Query the store for the URN of the mount point */
963 	iri = tracker_miner_fs_query_urn (TRACKER_MINER_FS (miner),
964 	                                  mount_point);
965 
966 	if (iri) {
967 		/* If exists, just return, nothing else to do */
968 		g_message ("Mount point '%s' already exists in store: '%s'",
969 		           uri, iri);
970 		g_free (iri);
971 	} else {
972 		/* If it doesn't exist, we need to create it */
973 		g_message ("Mount point '%s' does not exist in store, need to create it",
974 		           uri);
975 
976 		/* Create a nfo:Folder for the mount point */
977 		g_string_append_printf (accumulator,
978 		                        "INSERT SILENT INTO <" TRACKER_OWN_GRAPH_URN "> {"
979 		                        " _:file a nfo:FileDataObject, nie:InformationElement, nfo:Folder ; "
980 		                        "        nie:isStoredAs _:file ; "
981 		                        "        nie:url \"%s\" ; "
982 		                        "        nie:mimeType \"inode/directory\" ; "
983 		                        "        nfo:fileLastModified \"1981-06-05T02:20:00Z\" . "
984 		                        "}",
985 		                        uri);
986 	}
987 
988 	g_free (uri);
989 }
990 
991 static void
set_up_mount_point_cb(GObject * source,GAsyncResult * result,gpointer user_data)992 set_up_mount_point_cb (GObject      *source,
993                        GAsyncResult *result,
994                        gpointer      user_data)
995 {
996 	TrackerSparqlConnection *connection = TRACKER_SPARQL_CONNECTION (source);
997 	gchar *removable_device_urn = user_data;
998 	GError *error = NULL;
999 
1000 	tracker_sparql_connection_update_finish (connection, result, &error);
1001 
1002 	if (error) {
1003 		g_critical ("Could not set mount point in database '%s', %s",
1004 		            removable_device_urn,
1005 		            error->message);
1006 		g_error_free (error);
1007 	}
1008 
1009 	g_free (removable_device_urn);
1010 }
1011 
1012 static void
set_up_mount_point_type(TrackerMinerFiles * miner,const gchar * removable_device_urn,gboolean removable,gboolean optical,GString * accumulator)1013 set_up_mount_point_type (TrackerMinerFiles *miner,
1014                          const gchar       *removable_device_urn,
1015                          gboolean           removable,
1016                          gboolean           optical,
1017                          GString           *accumulator)
1018 {
1019 	if (!accumulator) {
1020 		return;
1021 	}
1022 
1023 	g_debug ("Mount point type being set in DB for URN '%s'",
1024 	         removable_device_urn);
1025 
1026 	g_string_append_printf (accumulator,
1027 	                        "DELETE { <%s> tracker:isRemovable ?unknown } WHERE { <%s> a tracker:Volume; tracker:isRemovable ?unknown } ",
1028 	                        removable_device_urn, removable_device_urn);
1029 
1030 	g_string_append_printf (accumulator,
1031 	                        "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> { <%s> a tracker:Volume; tracker:isRemovable %s } ",
1032 	                        removable_device_urn, removable ? "true" : "false");
1033 
1034 	g_string_append_printf (accumulator,
1035 	                        "DELETE { <%s> tracker:isOptical ?unknown } WHERE { <%s> a tracker:Volume; tracker:isOptical ?unknown } ",
1036 	                        removable_device_urn, removable_device_urn);
1037 
1038 	g_string_append_printf (accumulator,
1039 	                        "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> { <%s> a tracker:Volume; tracker:isOptical %s } ",
1040 	                        removable_device_urn, optical ? "true" : "false");
1041 }
1042 
1043 static void
set_up_mount_point(TrackerMinerFiles * miner,const gchar * removable_device_urn,const gchar * mount_point,const gchar * mount_name,gboolean mounted,GString * accumulator)1044 set_up_mount_point (TrackerMinerFiles *miner,
1045                     const gchar       *removable_device_urn,
1046                     const gchar       *mount_point,
1047                     const gchar       *mount_name,
1048                     gboolean           mounted,
1049                     GString           *accumulator)
1050 {
1051 	GString *queries;
1052 
1053 	queries = g_string_new (NULL);
1054 
1055 	if (mounted) {
1056 		g_debug ("Mount point state (MOUNTED) being set in DB for URN '%s' (mount_point: %s)",
1057 		         removable_device_urn,
1058 		         mount_point ? mount_point : "unknown");
1059 
1060 		if (mount_point) {
1061 			GFile *file;
1062 			gchar *uri;
1063 
1064 			file = g_file_new_for_path (mount_point);
1065 			uri = g_file_get_uri (file);
1066 
1067 			/* Before assigning a nfo:FileDataObject as tracker:mountPoint for
1068 			 * the volume, make sure the nfo:FileDataObject exists in the store */
1069 			ensure_mount_point_exists (miner, file, queries);
1070 
1071 			g_string_append_printf (queries,
1072 			                        "DELETE { "
1073 			                        "  <%s> tracker:mountPoint ?u "
1074 			                        "} WHERE { "
1075 			                        "  ?u a nfo:FileDataObject; "
1076 			                        "     nie:url \"%s\" "
1077 			                        "} ",
1078 			                        removable_device_urn, uri);
1079 
1080 			g_string_append_printf (queries,
1081 			                        "DELETE { <%s> a rdfs:Resource }  "
1082 			                        "INSERT { "
1083 			                        "  <%s> a tracker:Volume; "
1084 			                        "       tracker:mountPoint ?u "
1085 			                        "} WHERE { "
1086 			                        "  ?u a nfo:FileDataObject; "
1087 			                        "     nie:url \"%s\" "
1088 			                        "} ",
1089 			                        removable_device_urn, removable_device_urn, uri);
1090 
1091 			g_object_unref (file);
1092 			g_free (uri);
1093 		}
1094 
1095 		g_string_append_printf (queries,
1096 		                        "DELETE { <%s> tracker:isMounted ?unknown } WHERE { <%s> a tracker:Volume; tracker:isMounted ?unknown } ",
1097 		                        removable_device_urn, removable_device_urn);
1098 
1099                 if (mount_name) {
1100                         g_string_append_printf (queries,
1101                                                 "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> { <%s> a tracker:Volume; tracker:isMounted true; nie:title \"%s\" } ",
1102                                                 removable_device_urn, mount_name);
1103                 } else {
1104                         g_string_append_printf (queries,
1105                                                 "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> { <%s> a tracker:Volume; tracker:isMounted true } ",
1106                                                 removable_device_urn);
1107                 }
1108 
1109                 g_string_append_printf (queries,
1110                                         "INSERT { GRAPH <" TRACKER_OWN_GRAPH_URN "> { ?do tracker:available true } } WHERE { ?do nie:dataSource <%s> } ",
1111                                         removable_device_urn);
1112 	} else {
1113 		gchar *now;
1114 
1115 		g_debug ("Mount point state (UNMOUNTED) being set in DB for URN '%s'",
1116 		         removable_device_urn);
1117 
1118 		now = tracker_date_to_string (time (NULL));
1119 
1120 		g_string_append_printf (queries,
1121 		                        "DELETE { <%s> tracker:unmountDate ?unknown } WHERE { <%s> a tracker:Volume; tracker:unmountDate ?unknown } ",
1122 		                        removable_device_urn, removable_device_urn);
1123 
1124 		g_string_append_printf (queries,
1125 		                        "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> { <%s> a tracker:Volume; tracker:unmountDate \"%s\" } ",
1126 		                        removable_device_urn, now);
1127 
1128 		g_string_append_printf (queries,
1129 		                        "DELETE { <%s> tracker:isMounted ?unknown } WHERE { <%s> a tracker:Volume; tracker:isMounted ?unknown } ",
1130 		                        removable_device_urn, removable_device_urn);
1131 
1132 		g_string_append_printf (queries,
1133 		                        "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> { <%s> a tracker:Volume; tracker:isMounted false } ",
1134 		                        removable_device_urn);
1135 
1136 		g_string_append_printf (queries,
1137 		                        "DELETE { ?do tracker:available true } WHERE { ?do nie:dataSource <%s> } ",
1138 		                        removable_device_urn);
1139 
1140 		g_free (now);
1141 	}
1142 
1143 	if (accumulator) {
1144 		g_string_append_printf (accumulator, "%s ", queries->str);
1145 	} else {
1146 		tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
1147 		                                        queries->str,
1148 		                                        G_PRIORITY_LOW,
1149 		                                        NULL,
1150 		                                        set_up_mount_point_cb,
1151 		                                        g_strdup (removable_device_urn));
1152 	}
1153 
1154 	g_string_free (queries, TRUE);
1155 }
1156 
1157 static void
init_mount_points_cb(GObject * source,GAsyncResult * result,gpointer user_data)1158 init_mount_points_cb (GObject      *source,
1159                       GAsyncResult *result,
1160                       gpointer      user_data)
1161 {
1162 	GError *error = NULL;
1163 
1164 	tracker_sparql_connection_update_finish (TRACKER_SPARQL_CONNECTION (source),
1165 	                                         result,
1166 	                                         &error);
1167 
1168 	if (error) {
1169 		g_critical ("Could not initialize currently active mount points: %s",
1170 		            error->message);
1171 		g_error_free (error);
1172 	} else {
1173 		/* Mount points correctly initialized */
1174 		(TRACKER_MINER_FILES (user_data))->private->mount_points_initialized = TRUE;
1175 		/* If this happened AFTER we have a proper config, initialize
1176 		 * stale volume removal now. */
1177 		if ((TRACKER_MINER_FILES (user_data))->private->config) {
1178 			init_stale_volume_removal (TRACKER_MINER_FILES (user_data));
1179 		}
1180 	}
1181 }
1182 
1183 static void
init_mount_points(TrackerMinerFiles * miner_files)1184 init_mount_points (TrackerMinerFiles *miner_files)
1185 {
1186 	TrackerMiner *miner = TRACKER_MINER (miner_files);
1187 	TrackerMinerFilesPrivate *priv;
1188 	GHashTable *volumes;
1189 	GHashTableIter iter;
1190 	gpointer key, value;
1191 	GString *accumulator;
1192 	GError *error = NULL;
1193 	TrackerSparqlCursor *cursor;
1194 	GSList *uuids, *u;
1195 
1196 	g_debug ("Initializing mount points...");
1197 
1198 	/* First, get all mounted volumes, according to tracker-store (SYNC!) */
1199 	cursor = tracker_sparql_connection_query (tracker_miner_get_connection (miner),
1200 	                                          "SELECT ?v WHERE { ?v a tracker:Volume ; tracker:isMounted true }",
1201 	                                          NULL, &error);
1202 	if (error) {
1203 		g_critical ("Could not obtain the mounted volumes: %s", error->message);
1204 		g_error_free (error);
1205 		return;
1206 	}
1207 
1208 	priv = TRACKER_MINER_FILES_GET_PRIVATE (miner);
1209 
1210 	volumes = g_hash_table_new_full (g_str_hash, g_str_equal,
1211 	                                 (GDestroyNotify) g_free,
1212 	                                 NULL);
1213 
1214 
1215 	/* Make sure the root partition is always set to mounted, as GIO won't
1216 	 * report it as a proper mount */
1217 	g_hash_table_insert (volumes,
1218 	                     g_strdup (TRACKER_DATASOURCE_URN_NON_REMOVABLE_MEDIA),
1219 	                     GINT_TO_POINTER (VOLUME_MOUNTED));
1220 
1221 	while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
1222 		gint state;
1223 		const gchar *urn;
1224 
1225 		state = VOLUME_MOUNTED_IN_STORE;
1226 
1227 		urn = tracker_sparql_cursor_get_string (cursor, 0, NULL);
1228 
1229 		if (!urn)
1230 			continue;
1231 
1232 		if (strcmp (urn, TRACKER_DATASOURCE_URN_NON_REMOVABLE_MEDIA) == 0) {
1233 			/* Report non-removable media to be mounted by HAL as well */
1234 			state |= VOLUME_MOUNTED;
1235 		}
1236 
1237 		g_hash_table_replace (volumes, g_strdup (urn), GINT_TO_POINTER (state));
1238 	}
1239 
1240 	g_object_unref (cursor);
1241 
1242 	/* Then, get all currently mounted non-REMOVABLE volumes, according to GIO */
1243 	uuids = tracker_storage_get_device_uuids (priv->storage, 0, TRUE);
1244 	for (u = uuids; u; u = u->next) {
1245 		const gchar *uuid;
1246 		gchar *non_removable_device_urn;
1247 		gint state;
1248 
1249 		uuid = u->data;
1250 		non_removable_device_urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
1251 
1252 		state = GPOINTER_TO_INT (g_hash_table_lookup (volumes, non_removable_device_urn));
1253 		state |= VOLUME_MOUNTED;
1254 
1255 		g_hash_table_replace (volumes, non_removable_device_urn, GINT_TO_POINTER (state));
1256 	}
1257 
1258 	g_slist_foreach (uuids, (GFunc) g_free, NULL);
1259 	g_slist_free (uuids);
1260 
1261 	/* Then, get all currently mounted REMOVABLE volumes, according to GIO */
1262 	if (priv->index_removable_devices) {
1263 		uuids = tracker_storage_get_device_uuids (priv->storage, TRACKER_STORAGE_REMOVABLE, FALSE);
1264 		for (u = uuids; u; u = u->next) {
1265 			const gchar *uuid;
1266 			gchar *removable_device_urn;
1267 			gint state;
1268 
1269 			uuid = u->data;
1270 			removable_device_urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
1271 
1272 			state = GPOINTER_TO_INT (g_hash_table_lookup (volumes, removable_device_urn));
1273 			state |= VOLUME_MOUNTED;
1274 
1275 			g_hash_table_replace (volumes, removable_device_urn, GINT_TO_POINTER (state));
1276 		}
1277 
1278 		g_slist_foreach (uuids, (GFunc) g_free, NULL);
1279 		g_slist_free (uuids);
1280 	}
1281 
1282 	accumulator = g_string_new (NULL);
1283 	g_hash_table_iter_init (&iter, volumes);
1284 
1285 	/* Finally, set up volumes based on the composed info */
1286 	while (g_hash_table_iter_next (&iter, &key, &value)) {
1287 		const gchar *urn = key;
1288 		gint state = GPOINTER_TO_INT (value);
1289 
1290 		if ((state & VOLUME_MOUNTED) &&
1291 		    !(state & VOLUME_MOUNTED_IN_STORE)) {
1292 			const gchar *mount_point = NULL;
1293 			TrackerStorageType type = 0;
1294 
1295 			/* Note: is there any case where the urn doesn't have our
1296 			 *  datasource prefix? */
1297 			if (g_str_has_prefix (urn, TRACKER_PREFIX_DATASOURCE_URN)) {
1298 				const gchar *uuid;
1299 
1300 				uuid = urn + strlen (TRACKER_PREFIX_DATASOURCE_URN);
1301 				mount_point = tracker_storage_get_mount_point_for_uuid (priv->storage, uuid);
1302 				type = tracker_storage_get_type_for_uuid (priv->storage, uuid);
1303 			}
1304 
1305 			if (urn) {
1306 				if (mount_point) {
1307 					g_debug ("Mount point state incorrect in DB for URN '%s', "
1308 					         "currently it is mounted on '%s'",
1309 					         urn,
1310 					         mount_point);
1311 				} else {
1312 					g_debug ("Mount point state incorrect in DB for URN '%s', "
1313 					         "currently it is mounted",
1314 					         urn);
1315 				}
1316 
1317 				/* Set mount point state */
1318 				set_up_mount_point (TRACKER_MINER_FILES (miner),
1319 				                    urn,
1320 				                    mount_point,
1321                                                     NULL,
1322 				                    TRUE,
1323 				                    accumulator);
1324 
1325 				/* Set mount point type */
1326 				set_up_mount_point_type (TRACKER_MINER_FILES (miner),
1327 				                         urn,
1328 				                         TRACKER_STORAGE_TYPE_IS_REMOVABLE (type),
1329 				                         TRACKER_STORAGE_TYPE_IS_OPTICAL (type),
1330 				                         accumulator);
1331 
1332 				if (mount_point) {
1333 					TrackerIndexingTree *indexing_tree;
1334 					TrackerDirectoryFlags flags;
1335 					GFile *file;
1336 
1337 					indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner));
1338 					flags = TRACKER_DIRECTORY_FLAG_RECURSE |
1339 						TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
1340 						TRACKER_DIRECTORY_FLAG_PRESERVE;
1341 
1342 					if (tracker_config_get_enable_monitors (miner_files->private->config)) {
1343 						flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1344 					}
1345 
1346 					/* Add the current mount point as reported to have incorrect
1347 					 * state. We will force mtime checks on this mount points,
1348 					 * even if no-mtime-check-needed was set. */
1349 					file = g_file_new_for_path (mount_point);
1350 					if (tracker_miner_files_is_file_eligible (miner_files, file)) {
1351 						tracker_indexing_tree_add (indexing_tree,
1352 									   file,
1353 									   flags);
1354 					}
1355 					g_object_unref (file);
1356 				}
1357 			}
1358 		} else if (!(state & VOLUME_MOUNTED) &&
1359 		           (state & VOLUME_MOUNTED_IN_STORE)) {
1360 			if (urn) {
1361 				g_debug ("Mount point state incorrect in DB for URN '%s', "
1362 				         "currently it is NOT mounted",
1363 				         urn);
1364 				set_up_mount_point (TRACKER_MINER_FILES (miner),
1365 				                    urn,
1366 				                    NULL,
1367                                                     NULL,
1368 				                    FALSE,
1369 				                    accumulator);
1370 				/* There's no need to force mtime check in these inconsistent
1371 				 * mount points, as they are not mounted right now. */
1372 			}
1373 		}
1374 	}
1375 
1376 	if (accumulator->str[0] != '\0') {
1377 		tracker_sparql_connection_update_async (tracker_miner_get_connection (miner),
1378 		                                        accumulator->str,
1379 		                                        G_PRIORITY_LOW,
1380 		                                        NULL,
1381 		                                        init_mount_points_cb,
1382 		                                        miner);
1383 	} else {
1384 		/* Note. Not initializing stale volume removal timeout because
1385 		 * we do not have the configuration setup yet */
1386 		(TRACKER_MINER_FILES (miner))->private->mount_points_initialized = TRUE;
1387 	}
1388 
1389 	g_string_free (accumulator, TRUE);
1390 	g_hash_table_unref (volumes);
1391 }
1392 
1393 static gboolean
cleanup_stale_removable_volumes_cb(gpointer user_data)1394 cleanup_stale_removable_volumes_cb (gpointer user_data)
1395 {
1396 	TrackerMinerFiles *miner = TRACKER_MINER_FILES (user_data);
1397 	gint n_days_threshold;
1398 	time_t n_days_ago;
1399 	gchar *n_days_ago_as_string;
1400 
1401 	n_days_threshold = tracker_config_get_removable_days_threshold (miner->private->config);
1402 
1403 	if (n_days_threshold == 0)
1404 		return TRUE;
1405 
1406 	n_days_ago = (time (NULL) - (SECONDS_PER_DAY * n_days_threshold));
1407 	n_days_ago_as_string = tracker_date_to_string (n_days_ago);
1408 
1409 	g_message ("Running stale volumes check...");
1410 
1411 	miner_files_in_removable_media_remove_by_date (miner, n_days_ago_as_string);
1412 
1413 	g_free (n_days_ago_as_string);
1414 
1415 	return TRUE;
1416 }
1417 
1418 static void
init_stale_volume_removal(TrackerMinerFiles * miner)1419 init_stale_volume_removal (TrackerMinerFiles *miner)
1420 {
1421 	/* If disabled, make sure we don't do anything */
1422 	if (tracker_config_get_removable_days_threshold (miner->private->config) == 0) {
1423 		g_message ("Stale volume check is disabled");
1424 		return;
1425 	}
1426 
1427 	/* Run right away the first check */
1428 	cleanup_stale_removable_volumes_cb (miner);
1429 
1430 	g_message ("Initializing stale volume check timeout...");
1431 
1432 	/* Then, setup new timeout event every day */
1433 	miner->private->stale_volumes_check_id =
1434 		g_timeout_add_seconds (SECONDS_PER_DAY + 1,
1435 		                       cleanup_stale_removable_volumes_cb,
1436 		                       miner);
1437 }
1438 
1439 
1440 static void
mount_point_removed_cb(TrackerStorage * storage,const gchar * uuid,const gchar * mount_point,gpointer user_data)1441 mount_point_removed_cb (TrackerStorage *storage,
1442                         const gchar    *uuid,
1443                         const gchar    *mount_point,
1444                         gpointer        user_data)
1445 {
1446 	TrackerMinerFiles *miner = user_data;
1447 	TrackerIndexingTree *indexing_tree;
1448 	gchar *urn;
1449 	GFile *mount_point_file;
1450 
1451 	urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
1452 	g_debug ("Mount point removed for URN '%s'", urn);
1453 
1454 	mount_point_file = g_file_new_for_path (mount_point);
1455 
1456 	/* Tell TrackerMinerFS to skip monitoring everything under the mount
1457 	 *  point (in case there was no pre-unmount notification) */
1458 	indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner));
1459 	tracker_indexing_tree_remove (indexing_tree, mount_point_file);
1460 
1461 	/* Set mount point status in tracker-store */
1462 	set_up_mount_point (miner, urn, mount_point, NULL, FALSE, NULL);
1463 
1464 	g_free (urn);
1465 	g_object_unref (mount_point_file);
1466 }
1467 
1468 static void
mount_point_added_cb(TrackerStorage * storage,const gchar * uuid,const gchar * mount_point,const gchar * mount_name,gboolean removable,gboolean optical,gpointer user_data)1469 mount_point_added_cb (TrackerStorage *storage,
1470                       const gchar    *uuid,
1471                       const gchar    *mount_point,
1472                       const gchar    *mount_name,
1473                       gboolean        removable,
1474                       gboolean        optical,
1475                       gpointer        user_data)
1476 {
1477 	TrackerMinerFiles *miner = user_data;
1478 	TrackerMinerFilesPrivate *priv;
1479 	gchar *urn;
1480 	GString *queries;
1481 
1482 	priv = TRACKER_MINER_FILES_GET_PRIVATE (miner);
1483 
1484 	urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
1485 	g_message ("Mount point added for URN '%s'", urn);
1486 
1487 	if (removable && !priv->index_removable_devices) {
1488 		g_message ("  Not crawling, removable devices disabled in config");
1489 	} else if (optical && !priv->index_optical_discs) {
1490 		g_message ("  Not crawling, optical devices discs disabled in config");
1491 	} else if (!removable && !optical) {
1492 		TrackerIndexingTree *indexing_tree;
1493 		TrackerDirectoryFlags flags;
1494 		GFile *mount_point_file;
1495 		GSList *l;
1496 
1497 		mount_point_file = g_file_new_for_path (mount_point);
1498 		indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner));
1499 
1500 		/* Check if one of the recursively indexed locations is in
1501 		 *   the mounted path, or if the mounted path is inside
1502 		 *   a recursively indexed directory... */
1503 		for (l = tracker_config_get_index_recursive_directories (miner->private->config);
1504 		     l;
1505 		     l = g_slist_next (l)) {
1506 			GFile *config_file;
1507 
1508 			config_file = g_file_new_for_path (l->data);
1509 			flags = TRACKER_DIRECTORY_FLAG_RECURSE |
1510 				TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
1511 				TRACKER_DIRECTORY_FLAG_PRESERVE;
1512 
1513 			if (tracker_config_get_enable_monitors (miner->private->config)) {
1514 				flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1515 			}
1516 
1517 			if (g_file_equal (config_file, mount_point_file) ||
1518 			    g_file_has_prefix (config_file, mount_point_file)) {
1519 				/* If the config path is contained inside the mount path,
1520 				 *  then add the config path to re-check */
1521 				g_message ("  Re-check of configured path '%s' needed (recursively)",
1522 				           (gchar *) l->data);
1523 				tracker_indexing_tree_add (indexing_tree,
1524 							   config_file,
1525 							   flags);
1526 			} else if (g_file_has_prefix (mount_point_file, config_file)) {
1527 				/* If the mount path is contained inside the config path,
1528 				 *  then add the mount path to re-check */
1529 				g_message ("  Re-check of path '%s' needed (inside configured path '%s')",
1530 				           mount_point,
1531 				           (gchar *) l->data);
1532 				tracker_indexing_tree_add (indexing_tree,
1533 							   config_file,
1534 							   flags);
1535 			}
1536 			g_object_unref (config_file);
1537 		}
1538 
1539 		/* Check if one of the non-recursively indexed locations is in
1540 		 *  the mount path... */
1541 		for (l = tracker_config_get_index_single_directories (miner->private->config);
1542 		     l;
1543 		     l = g_slist_next (l)) {
1544 			GFile *config_file;
1545 
1546 			flags = TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
1547 
1548 			if (tracker_config_get_enable_monitors (miner->private->config)) {
1549 				flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1550 			}
1551 
1552 			config_file = g_file_new_for_path (l->data);
1553 			if (g_file_equal (config_file, mount_point_file) ||
1554 			    g_file_has_prefix (config_file, mount_point_file)) {
1555 				g_message ("  Re-check of configured path '%s' needed (non-recursively)",
1556 				           (gchar *) l->data);
1557 				tracker_indexing_tree_add (indexing_tree,
1558 							   config_file,
1559 							   flags);
1560 			}
1561 			g_object_unref (config_file);
1562 		}
1563 
1564 		g_object_unref (mount_point_file);
1565 	} else {
1566 		g_message ("  Adding directories in removable/optical media to crawler's queue");
1567 		miner_files_add_removable_or_optical_directory (miner,
1568 		                                                mount_point,
1569 		                                                uuid);
1570 	}
1571 
1572 	queries = g_string_new ("");
1573 	set_up_mount_point (miner, urn, mount_point, mount_name, TRUE, queries);
1574 	set_up_mount_point_type (miner, urn, removable, optical, queries);
1575 	tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
1576 	                                        queries->str,
1577 	                                        G_PRIORITY_LOW,
1578 	                                        NULL,
1579 	                                        set_up_mount_point_cb,
1580 	                                        g_strdup (urn));
1581 	g_string_free (queries, TRUE);
1582 	g_free (urn);
1583 }
1584 
1585 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
1586 
1587 static void
set_up_throttle(TrackerMinerFiles * mf,gboolean enable)1588 set_up_throttle (TrackerMinerFiles *mf,
1589                  gboolean           enable)
1590 {
1591 	gdouble throttle;
1592 	gint config_throttle;
1593 
1594 	config_throttle = tracker_config_get_throttle (mf->private->config);
1595 	throttle = (1.0 / 20) * config_throttle;
1596 
1597 	if (enable) {
1598 		throttle += 0.25;
1599 	}
1600 
1601 	throttle = CLAMP (throttle, 0, 1);
1602 
1603 	g_debug ("Setting new throttle to %0.3f", throttle);
1604 	tracker_miner_fs_set_throttle (TRACKER_MINER_FS (mf), throttle);
1605 }
1606 
1607 static void
check_battery_status(TrackerMinerFiles * mf)1608 check_battery_status (TrackerMinerFiles *mf)
1609 {
1610 	gboolean on_battery, on_low_battery;
1611 	gboolean should_pause = FALSE;
1612 	gboolean should_throttle = FALSE;
1613 
1614 	if (mf->private->power == NULL) {
1615 		return;
1616 	}
1617 
1618 	on_low_battery = tracker_power_get_on_low_battery (mf->private->power);
1619 	on_battery = tracker_power_get_on_battery (mf->private->power);
1620 
1621 	if (!on_battery) {
1622 		g_message ("Running on AC power");
1623 		should_pause = FALSE;
1624 		should_throttle = FALSE;
1625 	} else if (on_low_battery) {
1626 		g_message ("Running on LOW Battery, pausing");
1627 		should_pause = TRUE;
1628 		should_throttle = TRUE;
1629 	} else {
1630 		should_throttle = TRUE;
1631 
1632 		/* Check if miner should be paused based on configuration */
1633 		if (!tracker_config_get_index_on_battery (mf->private->config)) {
1634 			if (!tracker_config_get_index_on_battery_first_time (mf->private->config)) {
1635 				g_message ("Running on battery, but not enabled, pausing");
1636 				should_pause = TRUE;
1637 			} else if (tracker_miner_files_get_first_index_done ()) {
1638 				g_message ("Running on battery and first-time index "
1639 				           "already done, pausing");
1640 				should_pause = TRUE;
1641 			} else {
1642 				g_message ("Running on battery, but first-time index not "
1643 				           "already finished, keeping on");
1644 			}
1645 		} else {
1646 			g_message ("Running on battery");
1647 		}
1648 	}
1649 
1650 	if (should_pause) {
1651 		/* Don't try to pause again */
1652 		if (!mf->private->low_battery_pause) {
1653 			mf->private->low_battery_pause = TRUE;
1654 			tracker_miner_pause (TRACKER_MINER (mf));
1655 		}
1656 	} else {
1657 		/* Don't try to resume again */
1658 		if (mf->private->low_battery_pause) {
1659 			tracker_miner_resume (TRACKER_MINER (mf));
1660 			mf->private->low_battery_pause = FALSE;
1661 		}
1662 	}
1663 
1664 	set_up_throttle (mf, should_throttle);
1665 }
1666 
1667 /* Called when battery status change is detected */
1668 static void
battery_status_cb(GObject * object,GParamSpec * pspec,gpointer user_data)1669 battery_status_cb (GObject    *object,
1670                    GParamSpec *pspec,
1671                    gpointer    user_data)
1672 {
1673 	TrackerMinerFiles *mf = user_data;
1674 
1675 	check_battery_status (mf);
1676 }
1677 
1678 /* Called when battery-related configuration change is detected */
1679 static void
index_on_battery_cb(GObject * object,GParamSpec * pspec,gpointer user_data)1680 index_on_battery_cb (GObject    *object,
1681                      GParamSpec *pspec,
1682                      gpointer    user_data)
1683 {
1684 	TrackerMinerFiles *mf = user_data;
1685 
1686 	check_battery_status (mf);
1687 }
1688 
1689 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
1690 
1691 /* Called when mining has finished the first time */
1692 static void
miner_finished_cb(TrackerMinerFS * fs,gdouble seconds_elapsed,guint total_directories_found,guint total_directories_ignored,guint total_files_found,guint total_files_ignored,gpointer user_data)1693 miner_finished_cb (TrackerMinerFS *fs,
1694                    gdouble         seconds_elapsed,
1695                    guint           total_directories_found,
1696                    guint           total_directories_ignored,
1697                    guint           total_files_found,
1698                    guint           total_files_ignored,
1699                    gpointer        user_data)
1700 {
1701 	TrackerMinerFiles *mf = TRACKER_MINER_FILES (fs);
1702 
1703 	/* Create stamp file if not already there */
1704 	if (!tracker_miner_files_get_first_index_done ()) {
1705 		tracker_miner_files_set_first_index_done (TRUE);
1706 	}
1707 
1708 	/* And remove the signal handler so that it's not
1709 	 *  called again */
1710 	if (mf->private->finished_handler) {
1711 		g_signal_handler_disconnect (fs, mf->private->finished_handler);
1712 		mf->private->finished_handler = 0;
1713 	}
1714 
1715 #if defined(HAVE_UPOWER) || defined(HAVE_HAL)
1716 	check_battery_status (mf);
1717 #endif /* defined(HAVE_UPOWER) || defined(HAVE_HAL) */
1718 }
1719 
1720 static void
mount_pre_unmount_cb(GVolumeMonitor * volume_monitor,GMount * mount,TrackerMinerFiles * mf)1721 mount_pre_unmount_cb (GVolumeMonitor    *volume_monitor,
1722                       GMount            *mount,
1723                       TrackerMinerFiles *mf)
1724 {
1725 	TrackerIndexingTree *indexing_tree;
1726 	GFile *mount_root;
1727 	gchar *uri;
1728 
1729 	mount_root = g_mount_get_root (mount);
1730 	uri = g_file_get_uri (mount_root);
1731 	g_message ("Pre-unmount requested for '%s'", uri);
1732 
1733 	indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
1734 	tracker_indexing_tree_remove (indexing_tree, mount_root);
1735 	g_object_unref (mount_root);
1736 
1737 	g_free (uri);
1738 }
1739 
1740 static gboolean
disk_space_check(TrackerMinerFiles * mf)1741 disk_space_check (TrackerMinerFiles *mf)
1742 {
1743 	gint limit;
1744 	gchar *data_dir;
1745 	gdouble remaining;
1746 
1747 	limit = tracker_config_get_low_disk_space_limit (mf->private->config);
1748 
1749 	if (limit < 1) {
1750 		return FALSE;
1751 	}
1752 
1753 	/* Get % of remaining space in the partition where the cache is */
1754 	data_dir = g_build_filename (g_get_user_cache_dir (), "tracker", NULL);
1755 	remaining = tracker_file_system_get_remaining_space_percentage (data_dir);
1756 	g_free (data_dir);
1757 
1758 	if (remaining <= limit) {
1759 		g_message ("WARNING: Available disk space (%lf%%) is below "
1760 		           "configured threshold for acceptable working (%d%%)",
1761 		           remaining, limit);
1762 		return TRUE;
1763 	}
1764 
1765 	return FALSE;
1766 }
1767 
1768 static gboolean
disk_space_check_cb(gpointer user_data)1769 disk_space_check_cb (gpointer user_data)
1770 {
1771 	TrackerMinerFiles *mf = user_data;
1772 
1773 	if (disk_space_check (mf)) {
1774 		/* Don't try to pause again */
1775 		if (!mf->private->disk_space_pause) {
1776 			mf->private->disk_space_pause = TRUE;
1777 			tracker_miner_pause (TRACKER_MINER (mf));
1778 		}
1779 	} else {
1780 		/* Don't try to resume again */
1781 		if (mf->private->disk_space_pause) {
1782 			tracker_miner_resume (TRACKER_MINER (mf));
1783 			mf->private->disk_space_pause = FALSE;
1784 		}
1785 	}
1786 
1787 	return TRUE;
1788 }
1789 
1790 static void
disk_space_check_start(TrackerMinerFiles * mf)1791 disk_space_check_start (TrackerMinerFiles *mf)
1792 {
1793 	gint limit;
1794 
1795 	if (mf->private->disk_space_check_id != 0) {
1796 		return;
1797 	}
1798 
1799 	limit = tracker_config_get_low_disk_space_limit (mf->private->config);
1800 
1801 	if (limit != -1) {
1802 		g_message ("Starting disk space check for every %d seconds",
1803 		           DISK_SPACE_CHECK_FREQUENCY);
1804 		mf->private->disk_space_check_id =
1805 			g_timeout_add_seconds (DISK_SPACE_CHECK_FREQUENCY,
1806 			                       disk_space_check_cb,
1807 			                       mf);
1808 
1809 		/* Call the function now too to make sure we have an
1810 		 * initial value too!
1811 		 */
1812 		disk_space_check_cb (mf);
1813 	} else {
1814 		g_message ("Not setting disk space, configuration is set to -1 (disabled)");
1815 	}
1816 }
1817 
1818 static void
disk_space_check_stop(TrackerMinerFiles * mf)1819 disk_space_check_stop (TrackerMinerFiles *mf)
1820 {
1821 	if (mf->private->disk_space_check_id) {
1822 		g_message ("Stopping disk space check");
1823 		g_source_remove (mf->private->disk_space_check_id);
1824 		mf->private->disk_space_check_id = 0;
1825 	}
1826 }
1827 
1828 static void
low_disk_space_limit_cb(GObject * gobject,GParamSpec * arg1,gpointer user_data)1829 low_disk_space_limit_cb (GObject    *gobject,
1830                          GParamSpec *arg1,
1831                          gpointer    user_data)
1832 {
1833 	TrackerMinerFiles *mf = user_data;
1834 
1835 	disk_space_check_cb (mf);
1836 }
1837 
1838 static void
indexing_tree_update_filter(TrackerIndexingTree * indexing_tree,TrackerFilterType filter,GSList * new_elems)1839 indexing_tree_update_filter (TrackerIndexingTree *indexing_tree,
1840 			     TrackerFilterType    filter,
1841 			     GSList              *new_elems)
1842 {
1843 	tracker_indexing_tree_clear_filters (indexing_tree, filter);
1844 
1845 	while (new_elems) {
1846 		tracker_indexing_tree_add_filter (indexing_tree, filter,
1847 						  new_elems->data);
1848 		new_elems = new_elems->next;
1849 	}
1850 }
1851 
1852 static void
miner_files_update_filters(TrackerMinerFiles * files)1853 miner_files_update_filters (TrackerMinerFiles *files)
1854 {
1855 	TrackerIndexingTree *indexing_tree;
1856 	GSList *list;
1857 
1858 	indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (files));
1859 
1860 	/* Ignored files */
1861 	list = tracker_config_get_ignored_files (files->private->config);
1862 	indexing_tree_update_filter (indexing_tree, TRACKER_FILTER_FILE, list);
1863 
1864 	/* Ignored directories */
1865 	list = tracker_config_get_ignored_directories (files->private->config);
1866 	indexing_tree_update_filter (indexing_tree,
1867 				     TRACKER_FILTER_DIRECTORY,
1868 				     list);
1869 
1870 	/* Directories with content */
1871 	list = tracker_config_get_ignored_directories_with_content (files->private->config);
1872 	indexing_tree_update_filter (indexing_tree,
1873 				     TRACKER_FILTER_PARENT_DIRECTORY,
1874 				     list);
1875 }
1876 
1877 static void
update_directories_from_new_config(TrackerMinerFS * mf,GSList * new_dirs,GSList * old_dirs,gboolean recurse)1878 update_directories_from_new_config (TrackerMinerFS *mf,
1879                                     GSList         *new_dirs,
1880                                     GSList         *old_dirs,
1881                                     gboolean        recurse)
1882 {
1883 	TrackerMinerFilesPrivate *priv;
1884 	TrackerDirectoryFlags flags = 0;
1885 	TrackerIndexingTree *indexing_tree;
1886 	GSList *sl;
1887 
1888 	priv = TRACKER_MINER_FILES_GET_PRIVATE (mf);
1889 	indexing_tree = tracker_miner_fs_get_indexing_tree (mf);
1890 
1891 	g_message ("Updating %s directories changed from configuration",
1892 	           recurse ? "recursive" : "single");
1893 
1894 	/* First remove all directories removed from the config */
1895 	for (sl = old_dirs; sl; sl = sl->next) {
1896 		const gchar *path;
1897 
1898 		path = sl->data;
1899 
1900 		/* If we are not still in the list, remove the dir */
1901 		if (!tracker_string_in_gslist (path, new_dirs)) {
1902 			GFile *file;
1903 
1904 			g_message ("  Removing directory: '%s'", path);
1905 
1906 			file = g_file_new_for_path (path);
1907 
1908 			/* First, remove the preserve flag, it might be
1909 			 * set on configuration directories within mount
1910 			 * points, as data should be persistent across
1911 			 * unmounts.
1912 			 */
1913 			tracker_indexing_tree_get_root (indexing_tree,
1914 							file, &flags);
1915 
1916 			if ((flags & TRACKER_DIRECTORY_FLAG_PRESERVE) != 0) {
1917 				flags &= ~(TRACKER_DIRECTORY_FLAG_PRESERVE);
1918 				tracker_indexing_tree_add (indexing_tree,
1919 							   file, flags);
1920 			}
1921 
1922 			/* Fully remove item (monitors and from store),
1923 			 * now that there's no preserve flag.
1924 			 */
1925 			tracker_indexing_tree_remove (indexing_tree, file);
1926 			g_object_unref (file);
1927 		}
1928 	}
1929 
1930 	flags = TRACKER_DIRECTORY_FLAG_NONE;
1931 
1932 	if (recurse) {
1933 		flags |= TRACKER_DIRECTORY_FLAG_RECURSE;
1934 	}
1935 
1936 	if (tracker_config_get_enable_monitors (priv->config)) {
1937 		flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
1938 	}
1939 
1940 	if (priv->mtime_check) {
1941 		flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
1942 	}
1943 
1944 	/* Second add directories which are new */
1945 	for (sl = new_dirs; sl; sl = sl->next) {
1946 		const gchar *path;
1947 
1948 		path = sl->data;
1949 
1950 		/* If we are now in the list, add the dir */
1951 		if (!tracker_string_in_gslist (path, old_dirs)) {
1952 			GFile *file;
1953 
1954 			g_message ("  Adding directory:'%s'", path);
1955 
1956 			file = g_file_new_for_path (path);
1957 			tracker_indexing_tree_add (indexing_tree, file, flags);
1958 			g_object_unref (file);
1959 		}
1960 	}
1961 }
1962 
1963 static void
index_recursive_directories_cb(GObject * gobject,GParamSpec * arg1,gpointer user_data)1964 index_recursive_directories_cb (GObject    *gobject,
1965                                 GParamSpec *arg1,
1966                                 gpointer    user_data)
1967 {
1968 	TrackerMinerFilesPrivate *private;
1969 	GSList *new_dirs, *old_dirs;
1970 
1971 	private = TRACKER_MINER_FILES_GET_PRIVATE (user_data);
1972 
1973 	new_dirs = tracker_config_get_index_recursive_directories (private->config);
1974 	old_dirs = private->index_recursive_directories;
1975 
1976 	update_directories_from_new_config (TRACKER_MINER_FS (user_data),
1977 	                                    new_dirs,
1978 	                                    old_dirs,
1979 	                                    TRUE);
1980 
1981 	/* Re-set the stored config in case it changes again */
1982 	if (private->index_recursive_directories) {
1983 		g_slist_foreach (private->index_recursive_directories, (GFunc) g_free, NULL);
1984 		g_slist_free (private->index_recursive_directories);
1985 	}
1986 
1987 	private->index_recursive_directories = tracker_gslist_copy_with_string_data (new_dirs);
1988 }
1989 
1990 static void
index_single_directories_cb(GObject * gobject,GParamSpec * arg1,gpointer user_data)1991 index_single_directories_cb (GObject    *gobject,
1992                              GParamSpec *arg1,
1993                              gpointer    user_data)
1994 {
1995 	TrackerMinerFilesPrivate *private;
1996 	GSList *new_dirs, *old_dirs;
1997 
1998 	private = TRACKER_MINER_FILES_GET_PRIVATE (user_data);
1999 
2000 	new_dirs = tracker_config_get_index_single_directories (private->config);
2001 	old_dirs = private->index_single_directories;
2002 
2003 	update_directories_from_new_config (TRACKER_MINER_FS (user_data),
2004 	                                    new_dirs,
2005 	                                    old_dirs,
2006 	                                    FALSE);
2007 
2008 	/* Re-set the stored config in case it changes again */
2009 	if (private->index_single_directories) {
2010 		g_slist_foreach (private->index_single_directories, (GFunc) g_free, NULL);
2011 		g_slist_free (private->index_single_directories);
2012 	}
2013 
2014 	private->index_single_directories = tracker_gslist_copy_with_string_data (new_dirs);
2015 }
2016 
2017 static gboolean
miner_files_force_recheck_idle(gpointer user_data)2018 miner_files_force_recheck_idle (gpointer user_data)
2019 {
2020 	TrackerMinerFiles *miner_files = user_data;
2021 	TrackerIndexingTree *indexing_tree;
2022 	GList *roots, *l;
2023 
2024 	miner_files_update_filters (miner_files);
2025 
2026 	indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (miner_files));
2027 	roots = tracker_indexing_tree_list_roots (indexing_tree);
2028 
2029 	for (l = roots; l; l = l->next)	{
2030 		GFile *root = l->data;
2031 
2032 		tracker_indexing_tree_notify_update (indexing_tree, root, FALSE);
2033 	}
2034 
2035 	miner_files->private->force_recheck_id = 0;
2036 	g_list_free (roots);
2037 
2038 	return FALSE;
2039 }
2040 
2041 static void
trigger_recheck_cb(GObject * gobject,GParamSpec * arg1,gpointer user_data)2042 trigger_recheck_cb (GObject    *gobject,
2043                     GParamSpec *arg1,
2044                     gpointer    user_data)
2045 {
2046 	TrackerMinerFiles *mf = user_data;
2047 
2048 	g_message ("Ignored content related configuration changed, checking index...");
2049 
2050 	if (mf->private->force_recheck_id == 0) {
2051 		/* Set idle so multiple changes in the config lead to one recheck */
2052 		mf->private->force_recheck_id =
2053 			g_idle_add (miner_files_force_recheck_idle, mf);
2054 	}
2055 }
2056 
2057 static gboolean
index_volumes_changed_idle(gpointer user_data)2058 index_volumes_changed_idle (gpointer user_data)
2059 {
2060 	TrackerMinerFiles *mf = user_data;
2061 	GSList *mounts_removed = NULL;
2062 	GSList *mounts_added = NULL;
2063 	gboolean new_index_removable_devices;
2064 	gboolean new_index_optical_discs;
2065 
2066 	g_message ("Volume related configuration changed, updating...");
2067 
2068 	/* Read new config values. Note that if removable devices is FALSE,
2069 	 * optical discs will also always be FALSE. */
2070 	new_index_removable_devices = tracker_config_get_index_removable_devices (mf->private->config);
2071 	new_index_optical_discs = (new_index_removable_devices ?
2072 	                           tracker_config_get_index_optical_discs (mf->private->config) :
2073 	                           FALSE);
2074 
2075 	/* Removable devices config changed? */
2076 	if (mf->private->index_removable_devices != new_index_removable_devices) {
2077 		GSList *m;
2078 
2079 		/* Get list of roots for currently mounted removable devices
2080 		 * (excluding optical) */
2081 		m = tracker_storage_get_device_roots (mf->private->storage,
2082 		                                      TRACKER_STORAGE_REMOVABLE,
2083 		                                      TRUE);
2084 		/* Set new config value */
2085 		mf->private->index_removable_devices = new_index_removable_devices;
2086 
2087 		if (mf->private->index_removable_devices) {
2088 			/* If previously not indexing and now indexing, need to re-check
2089 			 * current mounted volumes, add new monitors and index new files
2090 			 */
2091 			mounts_added = m;
2092 		} else {
2093 			/* If previously indexing and now not indexing, need to re-check
2094 			 * current mounted volumes, remove monitors and remove all resources
2095 			 * from the store belonging to a removable device
2096 			 */
2097 			mounts_removed = m;
2098 
2099 			/* And now, single sparql update to remove all resources
2100 			 * corresponding to removable devices (includes those
2101 			 * not currently mounted) */
2102 			miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE);
2103 		}
2104 	}
2105 
2106 	/* Optical discs config changed? */
2107 	if (mf->private->index_optical_discs != new_index_optical_discs) {
2108 		GSList *m;
2109 
2110 		/* Get list of roots for removable devices (excluding optical) */
2111 		m = tracker_storage_get_device_roots (mf->private->storage,
2112 		                                      TRACKER_STORAGE_REMOVABLE | TRACKER_STORAGE_OPTICAL,
2113 		                                      TRUE);
2114 
2115 		/* Set new config value */
2116 		mf->private->index_optical_discs = new_index_optical_discs;
2117 
2118 		if (mf->private->index_optical_discs) {
2119 			/* If previously not indexing and now indexing, need to re-check
2120 			 * current mounted volumes, add new monitors and index new files
2121 			 */
2122 			mounts_added = g_slist_concat (mounts_added, m);
2123 		} else {
2124 			/* If previously indexing and now not indexing, need to re-check
2125 			 * current mounted volumes, remove monitors and remove all resources
2126 			 * from the store belonging to a optical disc
2127 			 */
2128 			mounts_removed = g_slist_concat (mounts_removed, m);
2129 
2130 			/* And now, single sparql update to remove all resources
2131 			 * corresponding to removable+optical devices (includes those
2132 			 * not currently mounted) */
2133 			miner_files_in_removable_media_remove_by_type (mf, TRACKER_STORAGE_REMOVABLE | TRACKER_STORAGE_OPTICAL);
2134 		}
2135 	}
2136 
2137 	/* Tell TrackerMinerFS to stop monitoring the given removed mount paths, if any */
2138 	if (mounts_removed) {
2139 		TrackerIndexingTree *indexing_tree;
2140 		GSList *sl;
2141 
2142 		indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
2143 
2144 		for (sl = mounts_removed; sl; sl = g_slist_next (sl)) {
2145 			GFile *mount_point_file;
2146 
2147 			mount_point_file = g_file_new_for_path (sl->data);
2148 			tracker_indexing_tree_remove (indexing_tree,
2149 						      mount_point_file);
2150 			g_object_unref (mount_point_file);
2151 		}
2152 
2153 		g_slist_foreach (mounts_removed, (GFunc) g_free, NULL);
2154 		g_slist_free (mounts_removed);
2155 	}
2156 
2157 	/* Tell TrackerMinerFS to start monitoring the given added mount paths, if any */
2158 	if (mounts_added) {
2159 		GSList *sl;
2160 
2161 		for (sl = mounts_added; sl; sl = g_slist_next (sl)) {
2162 			miner_files_add_removable_or_optical_directory (mf,
2163 			                                                (gchar *) sl->data,
2164 			                                                NULL);
2165 		}
2166 
2167 		g_slist_foreach (mounts_added, (GFunc) g_free, NULL);
2168 		g_slist_free (mounts_added);
2169 	}
2170 
2171 	mf->private->volumes_changed_id = 0;
2172 
2173 	/* Check if the stale volume removal configuration changed from enabled to disabled
2174 	 * or from disabled to enabled */
2175 	if (tracker_config_get_removable_days_threshold (mf->private->config) == 0 &&
2176 	    mf->private->stale_volumes_check_id != 0) {
2177 		/* From having the check enabled to having it disabled, remove the timeout */
2178 		g_debug ("  Stale volume removal now disabled, removing timeout");
2179 		g_source_remove (mf->private->stale_volumes_check_id);
2180 		mf->private->stale_volumes_check_id = 0;
2181 	} else if (tracker_config_get_removable_days_threshold (mf->private->config) > 0 &&
2182 	           mf->private->stale_volumes_check_id == 0) {
2183 		g_debug ("  Stale volume removal now enabled, initializing timeout");
2184 		/* From having the check disabled to having it enabled, so fire up the
2185 		 * timeout. */
2186 		init_stale_volume_removal (TRACKER_MINER_FILES (mf));
2187 	}
2188 
2189 	return FALSE;
2190 }
2191 
2192 static void
index_volumes_changed_cb(GObject * gobject,GParamSpec * arg1,gpointer user_data)2193 index_volumes_changed_cb (GObject    *gobject,
2194                           GParamSpec *arg1,
2195                           gpointer    user_data)
2196 {
2197 	TrackerMinerFiles *miner_files = user_data;
2198 
2199 	if (miner_files->private->volumes_changed_id == 0) {
2200 		/* Set idle so multiple changes in the config lead to one check */
2201 		miner_files->private->volumes_changed_id =
2202 			g_idle_add (index_volumes_changed_idle, miner_files);
2203 	}
2204 }
2205 
2206 static const gchar *
miner_files_get_file_urn(TrackerMinerFiles * miner,GFile * file,gboolean * is_iri)2207 miner_files_get_file_urn (TrackerMinerFiles *miner,
2208                           GFile             *file,
2209                           gboolean          *is_iri)
2210 {
2211 	const gchar *urn;
2212 
2213 	urn = tracker_miner_fs_get_urn (TRACKER_MINER_FS (miner), file);
2214 	*is_iri = TRUE;
2215 
2216 	if (!urn) {
2217 		/* This is a new insertion, use anonymous URNs to store files */
2218 		urn = "_:file";
2219 		*is_iri = FALSE;
2220 	}
2221 
2222 	return urn;
2223 }
2224 
2225 static void
miner_files_add_to_datasource(TrackerMinerFiles * mf,GFile * file,TrackerResource * resource)2226 miner_files_add_to_datasource (TrackerMinerFiles *mf,
2227                                GFile             *file,
2228                                TrackerResource   *resource)
2229 {
2230 	TrackerMinerFilesPrivate *priv;
2231 	const gchar *removable_device_uuid;
2232 	gchar *removable_device_urn;
2233 
2234 	priv = TRACKER_MINER_FILES_GET_PRIVATE (mf);
2235 
2236 	removable_device_uuid = tracker_storage_get_uuid_for_file (priv->storage, file);
2237 
2238 	if (removable_device_uuid) {
2239 		removable_device_urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s",
2240 		                                        removable_device_uuid);
2241 	} else {
2242 		removable_device_urn = g_strdup (TRACKER_DATASOURCE_URN_NON_REMOVABLE_MEDIA);
2243 	}
2244 
2245 	tracker_resource_set_uri (resource, "nie:dataSource", removable_device_urn);
2246 
2247 	tracker_resource_set_boolean (resource, "tracker:available", TRUE);
2248 
2249 	g_free (removable_device_urn);
2250 }
2251 
2252 static void
miner_files_add_rdf_types(TrackerResource * resource,GFile * file,const gchar * mime_type)2253 miner_files_add_rdf_types (TrackerResource *resource,
2254                            GFile           *file,
2255                            const gchar     *mime_type)
2256 {
2257 	GStrv rdf_types;
2258 	gint i = 0;
2259 
2260 	rdf_types = tracker_extract_module_manager_get_fallback_rdf_types (mime_type);
2261 
2262 	if (!rdf_types)
2263 		return;
2264 
2265 	while (rdf_types[i]) {
2266 		tracker_resource_add_uri (resource, "rdf:type", rdf_types[i]);
2267 		i++;
2268 	}
2269 
2270 	g_strfreev (rdf_types);
2271 }
2272 
2273 static void
process_file_data_free(ProcessFileData * data)2274 process_file_data_free (ProcessFileData *data)
2275 {
2276 	g_object_unref (data->miner);
2277 	g_object_unref (data->cancellable);
2278 	g_object_unref (data->file);
2279 	g_object_unref (data->task);
2280 	g_free (data->mime_type);
2281 	g_slice_free (ProcessFileData, data);
2282 }
2283 
2284 static gchar *
update_mount_point_sparql(ProcessFileData * data)2285 update_mount_point_sparql (ProcessFileData *data)
2286 {
2287 	const gchar *uuid;
2288 
2289 	uuid = g_object_get_qdata (G_OBJECT (data->file),
2290 	                           data->miner->private->quark_mount_point_uuid);
2291 
2292 	/* File represents a mount point */
2293 	if (G_UNLIKELY (uuid)) {
2294 		GString *queries;
2295 		gchar *removable_device_urn, *uri;
2296 
2297 		removable_device_urn = g_strdup_printf (TRACKER_PREFIX_DATASOURCE_URN "%s", uuid);
2298 		uri = g_file_get_uri (G_FILE (data->file));
2299 		queries = g_string_new ("");
2300 
2301 		g_string_append_printf (queries,
2302 		                        "DELETE { "
2303 		                        "  <%s> tracker:mountPoint ?unknown "
2304 		                        "} WHERE { "
2305 		                        "  <%s> a tracker:Volume; "
2306 		                        "       tracker:mountPoint ?unknown "
2307 		                        "} ",
2308 		                        removable_device_urn, removable_device_urn);
2309 
2310 		g_string_append_printf (queries,
2311 		                        "INSERT { GRAPH <%s> {"
2312 		                        "  <%s> a tracker:Volume; "
2313 		                        "       tracker:mountPoint ?u "
2314 		                        "} } WHERE { "
2315 		                        "  ?u a nfo:FileDataObject; "
2316 		                        "     nie:url \"%s\" "
2317 		                        "} ",
2318 		                        removable_device_urn, removable_device_urn, uri);
2319 
2320 		g_free (removable_device_urn);
2321 		g_free (uri);
2322 
2323 		return g_string_free (queries, FALSE);
2324 	}
2325 
2326 	return NULL;
2327 }
2328 
2329 static void
process_file_cb(GObject * object,GAsyncResult * result,gpointer user_data)2330 process_file_cb (GObject      *object,
2331                  GAsyncResult *result,
2332                  gpointer      user_data)
2333 {
2334 	TrackerMinerFilesPrivate *priv;
2335 	TrackerResource *resource;
2336 	ProcessFileData *data;
2337 	const gchar *mime_type, *urn;
2338 	gchar *parent_urn;
2339 	gchar *delete_properties_sparql = NULL, *mount_point_sparql;
2340 	GFileInfo *file_info;
2341 	guint64 time_;
2342 	GFile *file, *parent;
2343 	gchar *uri, *sparql_str, *sparql_update_str, *time_str;
2344 	GError *error = NULL;
2345 	gboolean is_iri;
2346 	gboolean is_directory;
2347 
2348 	data = user_data;
2349 	file = G_FILE (object);
2350 	file_info = g_file_query_info_finish (file, result, &error);
2351 	priv = TRACKER_MINER_FILES (data->miner)->private;
2352 
2353 	if (error) {
2354 		/* Something bad happened, notify about the error */
2355 		tracker_miner_fs_notify_finish (TRACKER_MINER_FS (data->miner), data->task, NULL, error);
2356 		priv->extraction_queue = g_list_remove (priv->extraction_queue, data);
2357 		process_file_data_free (data);
2358 		return;
2359 	}
2360 
2361 	uri = g_file_get_uri (file);
2362 	mime_type = g_file_info_get_content_type (file_info);
2363 	urn = miner_files_get_file_urn (TRACKER_MINER_FILES (data->miner), file, &is_iri);
2364 
2365 	data->mime_type = g_strdup (mime_type);
2366 
2367 	if (is_iri) {
2368 		/* Update: delete all statements inserted by miner except:
2369 		 *  - rdf:type statements as they could cause implicit deletion of user data
2370 		 *  - nie:contentCreated so it persists across updates
2371 		 *
2372 		 * Additionally, delete also nie:url as it might have been set by 3rd parties,
2373 		 * and it's used to know whether a file is known to tracker or not.
2374 		 */
2375 		delete_properties_sparql =
2376 			g_strdup_printf ("DELETE {"
2377 			                 "  GRAPH <%s> {"
2378 			                 "    <%s> ?p ?o"
2379 			                 "  } "
2380 			                 "} "
2381 			                 "WHERE {"
2382 			                 "  GRAPH <%s> {"
2383 			                 "    <%s> ?p ?o"
2384 			                 "    FILTER (?p != rdf:type && ?p != nie:contentCreated)"
2385 			                 "  } "
2386 			                 "} "
2387 			                 "DELETE {"
2388 			                 "  <%s> nie:url ?o"
2389 			                 "} WHERE {"
2390 			                 "  <%s> nie:url ?o"
2391 			                 "}",
2392 			                 TRACKER_OWN_GRAPH_URN, urn,
2393 			                 TRACKER_OWN_GRAPH_URN, urn,
2394 			                 urn, urn);
2395 	}
2396 
2397 	resource = tracker_resource_new (NULL);
2398 
2399 	if (is_iri) {
2400 		tracker_resource_set_identifier (resource, urn);
2401 	}
2402 
2403 	tracker_resource_add_uri (resource, "rdf:type", "nfo:FileDataObject");
2404 	tracker_resource_add_uri (resource, "rdf:type", "nie:InformationElement");
2405 
2406 	is_directory = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY ?
2407 	                TRUE : FALSE);
2408 	if (is_directory) {
2409 		tracker_resource_add_uri (resource, "rdf:type", "nfo:Folder");
2410 	}
2411 
2412 	parent = g_file_get_parent (file);
2413 	parent_urn = tracker_miner_fs_query_urn (TRACKER_MINER_FS (data->miner), parent);
2414 	g_object_unref (parent);
2415 
2416 	if (parent_urn) {
2417 		tracker_resource_set_uri (resource, "nfo:belongsToContainer", parent_urn);
2418 		g_free (parent_urn);
2419 	}
2420 
2421 	tracker_resource_set_string (resource, "nfo:fileName",
2422 	                             g_file_info_get_display_name (file_info));
2423 	tracker_resource_set_int64 (resource, "nfo:fileSize",
2424 	                            g_file_info_get_size (file_info));
2425 
2426 	time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2427 	time_str = tracker_date_to_string (time_);
2428 	tracker_resource_set_string (resource, "nfo:fileLastModified", time_str);
2429 	g_free (time_str);
2430 
2431 	time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2432 	time_str = tracker_date_to_string (time_);
2433 	tracker_resource_set_string (resource, "nfo:fileLastAccessed", time_str);
2434 	g_free (time_str);
2435 
2436 	/* Laying the link between the IE and the DO. We use IE = DO */
2437 	if (is_iri) {
2438 		tracker_resource_add_uri (resource, "nie:isStoredAs", urn);
2439 	} else {
2440 		tracker_resource_add_relation (resource, "nie:isStoredAs", resource);
2441 	}
2442 
2443 	/* The URL of the DataObject (because IE = DO, this is correct) */
2444 	tracker_resource_set_string (resource, "nie:url", uri);
2445 
2446 	tracker_resource_set_string (resource, "nie:mimeType", mime_type);
2447 
2448 	miner_files_add_to_datasource (data->miner, file, resource);
2449 
2450 	if (g_file_info_get_size (file_info) > 0)
2451 		miner_files_add_rdf_types (resource, file, mime_type);
2452 
2453 	mount_point_sparql = update_mount_point_sparql (data);
2454 	sparql_update_str = tracker_resource_print_sparql_update (resource, NULL, TRACKER_OWN_GRAPH_URN),
2455 	sparql_str = g_strdup_printf ("%s %s %s",
2456 	                              delete_properties_sparql ? delete_properties_sparql : "",
2457 	                              sparql_update_str,
2458 	                              mount_point_sparql ? mount_point_sparql : "");
2459 	g_free (delete_properties_sparql);
2460 	g_free (mount_point_sparql);
2461 
2462 	tracker_miner_fs_notify_finish (TRACKER_MINER_FS (data->miner), data->task,
2463 	                                sparql_str, NULL);
2464 
2465 	priv->extraction_queue = g_list_remove (priv->extraction_queue, data);
2466 	process_file_data_free (data);
2467 
2468 	g_object_run_dispose (G_OBJECT (resource));
2469 	g_object_unref (resource);
2470 	g_object_unref (file_info);
2471 	g_free (sparql_str);
2472 	g_free (uri);
2473 	g_free (sparql_update_str);
2474 }
2475 
2476 static gboolean
miner_files_process_file(TrackerMinerFS * fs,GFile * file,GTask * task)2477 miner_files_process_file (TrackerMinerFS *fs,
2478                           GFile          *file,
2479                           GTask          *task)
2480 {
2481 	TrackerMinerFilesPrivate *priv;
2482 	ProcessFileData *data;
2483 	const gchar *attrs;
2484 
2485 	data = g_slice_new0 (ProcessFileData);
2486 	data->miner = TRACKER_MINER_FILES (g_object_ref (fs));
2487 	data->cancellable = g_object_ref (g_task_get_cancellable (task));
2488 	data->file = g_object_ref (file);
2489 	data->task = g_object_ref (task);
2490 
2491 	priv = TRACKER_MINER_FILES (fs)->private;
2492 	priv->extraction_queue = g_list_prepend (priv->extraction_queue, data);
2493 
2494 	attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE ","
2495 		G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
2496 		G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
2497 		G_FILE_ATTRIBUTE_STANDARD_SIZE ","
2498 		G_FILE_ATTRIBUTE_TIME_MODIFIED ","
2499 		G_FILE_ATTRIBUTE_TIME_ACCESS;
2500 
2501 	g_file_query_info_async (file,
2502 	                         attrs,
2503 	                         G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2504 	                         G_PRIORITY_DEFAULT,
2505 	                         data->cancellable,
2506 	                         process_file_cb,
2507 	                         data);
2508 
2509 	return TRUE;
2510 }
2511 
2512 static void
process_file_attributes_cb(GObject * object,GAsyncResult * result,gpointer user_data)2513 process_file_attributes_cb (GObject      *object,
2514                             GAsyncResult *result,
2515                             gpointer      user_data)
2516 {
2517 	TrackerResource *resource;
2518 	ProcessFileData *data;
2519 	const gchar *urn;
2520 	GFileInfo *file_info;
2521 	guint64 time_;
2522 	GFile *file;
2523 	gchar *uri, *time_str, *sparql_str;
2524 	GError *error = NULL;
2525 	gboolean is_iri;
2526 
2527 	data = user_data;
2528 	file = G_FILE (object);
2529 	file_info = g_file_query_info_finish (file, result, &error);
2530 
2531 	if (error) {
2532 		/* Something bad happened, notify about the error */
2533 		tracker_miner_fs_notify_finish (TRACKER_MINER_FS (data->miner), data->task, NULL, error);
2534 		process_file_data_free (data);
2535 		return;
2536 	}
2537 
2538 	uri = g_file_get_uri (file);
2539 	urn = miner_files_get_file_urn (TRACKER_MINER_FILES (data->miner), file, &is_iri);
2540 
2541 	/* We MUST have an IRI in attributes updating */
2542 	if (!is_iri) {
2543 		error = g_error_new_literal (miner_files_error_quark,
2544 		                             0,
2545 		                             "Received request to update attributes but no IRI available!");
2546 		/* Notify about the error */
2547 		tracker_miner_fs_notify_finish (TRACKER_MINER_FS (data->miner), data->task, NULL, error);
2548 		process_file_data_free (data);
2549 		return;
2550 	}
2551 
2552 	resource = tracker_resource_new (urn);
2553 
2554 	/* Update nfo:fileLastModified */
2555 	time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
2556 	time_str = tracker_date_to_string (time_);
2557 	tracker_resource_set_string (resource, "nfo:fileLastModified", time_str);
2558 	g_free (time_str);
2559 
2560 	/* Update nfo:fileLastAccessed */
2561 	time_ = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_TIME_ACCESS);
2562 	time_str = tracker_date_to_string (time_);
2563 	tracker_resource_set_string (resource, "nfo:fileLastAccessed", time_str);
2564 	g_free (time_str);
2565 
2566 	g_object_unref (file_info);
2567 	g_free (uri);
2568 
2569 	/* Notify about the success */
2570 	sparql_str = tracker_resource_print_sparql_update (resource, NULL, TRACKER_OWN_GRAPH_URN);
2571 	tracker_miner_fs_notify_finish (TRACKER_MINER_FS (data->miner), data->task,
2572 	                                sparql_str, NULL);
2573 
2574 	process_file_data_free (data);
2575 	g_object_unref (resource);
2576 	g_free (sparql_str);
2577 }
2578 
2579 static gboolean
miner_files_process_file_attributes(TrackerMinerFS * fs,GFile * file,GTask * task)2580 miner_files_process_file_attributes (TrackerMinerFS *fs,
2581                                      GFile          *file,
2582                                      GTask          *task)
2583 {
2584 	ProcessFileData *data;
2585 	const gchar *attrs;
2586 
2587 	data = g_slice_new0 (ProcessFileData);
2588 	data->miner = TRACKER_MINER_FILES (g_object_ref (fs));
2589 	data->cancellable = g_object_ref (g_task_get_cancellable (task));
2590 	data->file = g_object_ref (file);
2591 	data->task = g_object_ref (task);
2592 
2593 	/* Query only attributes that may change in an ATTRIBUTES_UPDATED event */
2594 	attrs = G_FILE_ATTRIBUTE_TIME_MODIFIED ","
2595 		G_FILE_ATTRIBUTE_TIME_ACCESS;
2596 
2597 	g_file_query_info_async (file,
2598 	                         attrs,
2599 	                         G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2600 	                         G_PRIORITY_DEFAULT,
2601 	                         data->cancellable,
2602 	                         process_file_attributes_cb,
2603 	                         data);
2604 
2605 	return TRUE;
2606 }
2607 
2608 static void
miner_files_finished(TrackerMinerFS * fs,gdouble elapsed,gint directories_found,gint directories_ignored,gint files_found,gint files_ignored)2609 miner_files_finished (TrackerMinerFS *fs,
2610                       gdouble         elapsed,
2611                       gint            directories_found,
2612                       gint            directories_ignored,
2613                       gint            files_found,
2614                       gint            files_ignored)
2615 {
2616 	TrackerMinerFilesPrivate *priv = TRACKER_MINER_FILES (fs)->private;
2617 
2618 	if (priv->thumbnailer)
2619 		tracker_thumbnailer_send (priv->thumbnailer);
2620 
2621 	tracker_miner_files_set_last_crawl_done (TRUE);
2622 
2623 	tracker_miner_files_check_unextracted (TRACKER_MINER_FILES (fs));
2624 }
2625 
2626 static gchar *
create_delete_sparql(GFile * file,gboolean delete_self,gboolean delete_children)2627 create_delete_sparql (GFile    *file,
2628 		      gboolean  delete_self,
2629 		      gboolean  delete_children)
2630 {
2631 	GString *sparql;
2632 	gchar *uri;
2633 
2634 	g_return_val_if_fail (delete_self || delete_children, NULL);
2635 
2636 	uri = g_file_get_uri (file);
2637 	sparql = g_string_new ("DELETE {"
2638 			       "  ?f a rdfs:Resource . "
2639 			       "  ?ie a rdfs:Resource "
2640 			       "} WHERE {"
2641 			       "  ?f a rdfs:Resource ; "
2642 			       "     nie:url ?u . "
2643 			       "  ?ie nie:isStoredAs ?f . "
2644 			       "  FILTER (");
2645 
2646 	if (delete_self)
2647 		g_string_append_printf (sparql, "?u = \"%s\" ", uri);
2648 
2649 	if (delete_children) {
2650 		if (delete_self)
2651 			g_string_append (sparql, " || ");
2652 
2653 		g_string_append_printf (sparql, "STRSTARTS (?u, \"%s/\")", uri);
2654 	}
2655 
2656 	g_string_append (sparql, ")}");
2657 	g_free (uri);
2658 
2659 	return g_string_free (sparql, FALSE);
2660 }
2661 
2662 static gchar *
miner_files_remove_children(TrackerMinerFS * fs,GFile * file)2663 miner_files_remove_children (TrackerMinerFS *fs,
2664                              GFile          *file)
2665 {
2666 	return create_delete_sparql (file, FALSE, TRUE);
2667 }
2668 
2669 static gchar *
miner_files_remove_file(TrackerMinerFS * fs,GFile * file)2670 miner_files_remove_file (TrackerMinerFS *fs,
2671                          GFile          *file)
2672 {
2673 	TrackerMinerFilesPrivate *priv = TRACKER_MINER_FILES (fs)->private;
2674 
2675 	if (priv->thumbnailer) {
2676 		gchar *uri;
2677 
2678 		uri = g_file_get_uri (file);
2679 		tracker_thumbnailer_remove_add (priv->thumbnailer, uri, NULL);
2680 		g_free (uri);
2681 	}
2682 
2683 	return create_delete_sparql (file, TRUE, TRUE);
2684 }
2685 
2686 static void
move_thumbnails_cb(GObject * object,GAsyncResult * result,gpointer user_data)2687 move_thumbnails_cb (GObject      *object,
2688                     GAsyncResult *result,
2689                     gpointer      user_data)
2690 {
2691 	ThumbnailMoveData *data = user_data;
2692 	TrackerMinerFilesPrivate *priv = TRACKER_MINER_FILES (data->miner)->private;
2693 	GError *error = NULL;
2694 
2695 	TrackerSparqlCursor *cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object), result, &error);
2696 
2697 	if (error) {
2698 		g_critical ("Could move thumbnails: %s", error->message);
2699 		g_error_free (error);
2700 	} else {
2701 		while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
2702 			const gchar *src, *dst, *mimetype;
2703 
2704 			src = tracker_sparql_cursor_get_string (cursor, 0, NULL);
2705 			dst = tracker_sparql_cursor_get_string (cursor, 1, NULL);
2706 			mimetype = tracker_sparql_cursor_get_string (cursor, 2, NULL);
2707 
2708 			if (priv->thumbnailer) {
2709 				tracker_thumbnailer_move_add (priv->thumbnailer,
2710 				                              src, mimetype, dst);
2711 			}
2712 		}
2713 	}
2714 
2715 	g_object_unref (cursor);
2716 	g_main_loop_quit (data->main_loop);
2717 }
2718 
2719 static gchar *
miner_files_move_file(TrackerMinerFS * fs,GFile * file,GFile * source_file,gboolean recursive)2720 miner_files_move_file (TrackerMinerFS *fs,
2721                        GFile          *file,
2722                        GFile          *source_file,
2723                        gboolean        recursive)
2724 {
2725 	TrackerMinerFilesPrivate *priv = TRACKER_MINER_FILES (fs)->private;
2726 	GString *sparql = g_string_new (NULL);
2727 	const gchar *new_parent_iri;
2728 	gchar *uri, *source_uri, *display_name;
2729 	gchar *source_iri;
2730 	gchar *path, *basename;
2731 	GFile *new_parent;
2732 
2733 	uri = g_file_get_uri (file);
2734 	source_uri = g_file_get_uri (source_file);
2735 	source_iri = tracker_miner_fs_query_urn (fs, source_file);
2736 
2737 	if (priv->thumbnailer) {
2738 		GFileInfo *file_info;
2739 
2740 		file_info = g_file_query_info (file,
2741 		                               G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
2742 		                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2743 		                               NULL, NULL);
2744 		tracker_thumbnailer_move_add (priv->thumbnailer, source_uri,
2745 		                              g_file_info_get_content_type (file_info),
2746 					      uri);
2747 		g_object_unref (file_info);
2748 
2749 		if (recursive) {
2750 			ThumbnailMoveData move_data;
2751 			gchar *query;
2752 
2753 			g_debug ("Moving thumbnails within '%s'", uri);
2754 
2755 			/* Push all moved files to thumbnailer */
2756 			move_data.main_loop = g_main_loop_new (NULL, FALSE);
2757 			move_data.miner = TRACKER_MINER (fs);
2758 
2759 			query = g_strdup_printf ("SELECT ?url ?new_url nie:mimeType(?u) {"
2760 			                         "  ?u a rdfs:Resource ;"
2761 			                         "     nie:url ?url ."
2762 			                         "  BIND (CONCAT (\"%s/\", SUBSTR (?url, STRLEN (\"%s/\") + 1)) AS ?new_url) ."
2763 			                         "  FILTER (STRSTARTS (?url, \"%s/\"))"
2764 			                         "}",
2765 			                         uri, source_uri, source_uri);
2766 
2767 			tracker_sparql_connection_query_async (tracker_miner_get_connection (TRACKER_MINER (fs)),
2768 			                                       query,
2769 			                                       NULL,
2770 			                                       move_thumbnails_cb,
2771 			                                       &move_data);
2772 
2773 			g_main_loop_run (move_data.main_loop);
2774 			g_main_loop_unref (move_data.main_loop);
2775 			g_free (query);
2776 		}
2777 	}
2778 
2779 	path = g_file_get_path (file);
2780 	basename = g_filename_display_basename (path);
2781 	display_name = tracker_sparql_escape_string (basename);
2782 	g_free (basename);
2783 	g_free (path);
2784 
2785 	g_string_append_printf (sparql,
2786 	                        "DELETE { "
2787 	                        "  <%s> nfo:fileName ?f ; "
2788 	                        "       nie:url ?u ; "
2789 	                        "       nfo:belongsToContainer ?b"
2790 	                        "} WHERE { "
2791 	                        "  <%s> nfo:fileName ?f ; "
2792 	                        "       nie:url ?u . "
2793 	                        "       OPTIONAL { <%s> nfo:belongsToContainer ?b }"
2794 	                        "} ",
2795 	                        source_iri, source_iri, source_iri);
2796 
2797 	/* Get new parent information */
2798 	new_parent = g_file_get_parent (file);
2799 	new_parent_iri = tracker_miner_fs_query_urn (fs, new_parent);
2800 
2801 	g_string_append_printf (sparql,
2802 	                        "INSERT INTO <" TRACKER_OWN_GRAPH_URN "> {"
2803 	                        "  <%s> nfo:fileName \"%s\" ; "
2804 	                        "       nie:url \"%s\" ",
2805 	                        source_iri, display_name, uri);
2806 
2807 	if (new_parent && new_parent_iri) {
2808 		g_string_append_printf (sparql, "; nfo:belongsToContainer \"%s\"",
2809 		                        new_parent_iri);
2810 	}
2811 
2812 	g_string_append (sparql, "}");
2813 
2814 	if (recursive) {
2815 		g_string_append_printf (sparql,
2816 		                        " DELETE {"
2817 		                        "  ?u nie:url ?url "
2818 		                        "} INSERT { "
2819 		                        "  GRAPH <" TRACKER_OWN_GRAPH_URN "> {"
2820 		                        "    ?u nie:url ?new_url "
2821 		                        "  }"
2822 		                        "} WHERE {"
2823 		                        "  ?u a rdfs:Resource;"
2824 		                        "     nie:url ?url ."
2825 		                        "  BIND (CONCAT (\"%s/\", SUBSTR (?url, STRLEN (\"%s/\") + 1)) AS ?new_url) ."
2826 		                        "  FILTER (STRSTARTS (?url, \"%s/\"))"
2827 		                        "} ",
2828 		                        uri, source_uri, source_uri);
2829 	}
2830 
2831 	g_free (uri);
2832 	g_free (source_uri);
2833 	g_free (display_name);
2834 	g_clear_object (&new_parent);
2835 
2836 	return g_string_free (sparql, FALSE);
2837 }
2838 
2839 TrackerMiner *
tracker_miner_files_new(TrackerConfig * config,GError ** error)2840 tracker_miner_files_new (TrackerConfig  *config,
2841                          GError        **error)
2842 {
2843 	return g_initable_new (TRACKER_TYPE_MINER_FILES,
2844 	                       NULL,
2845 	                       error,
2846 	                       "root", NULL,
2847 	                       "config", config,
2848 	                       "processing-pool-wait-limit", 10,
2849 	                       "processing-pool-ready-limit", 100,
2850 	                       NULL);
2851 }
2852 
2853 gboolean
tracker_miner_files_check_file(GFile * file,GSList * ignored_file_paths,GSList * ignored_file_patterns)2854 tracker_miner_files_check_file (GFile  *file,
2855                                 GSList *ignored_file_paths,
2856                                 GSList *ignored_file_patterns)
2857 {
2858 	GSList *l;
2859 	gchar *basename;
2860 	gchar *path;
2861 	gboolean should_process;
2862 
2863 	should_process = FALSE;
2864 	basename = NULL;
2865 	path = NULL;
2866 
2867 	if (tracker_file_is_hidden (file)) {
2868 		/* Ignore hidden files */
2869 		goto done;
2870 	}
2871 
2872 	path = g_file_get_path (file);
2873 
2874 	for (l = ignored_file_paths; l; l = l->next) {
2875 		if (strcmp (l->data, path) == 0) {
2876 			goto done;
2877 		}
2878 	}
2879 
2880 	basename = g_file_get_basename (file);
2881 
2882 	for (l = ignored_file_patterns; l; l = l->next) {
2883 		if (g_pattern_match_string (l->data, basename)) {
2884 			goto done;
2885 		}
2886 	}
2887 
2888 	should_process = TRUE;
2889 
2890 done:
2891 	g_free (basename);
2892 	g_free (path);
2893 
2894 	return should_process;
2895 }
2896 
2897 gboolean
tracker_miner_files_check_directory(GFile * file,GSList * index_recursive_directories,GSList * index_single_directories,GSList * ignored_directory_paths,GSList * ignored_directory_patterns)2898 tracker_miner_files_check_directory (GFile  *file,
2899                                      GSList *index_recursive_directories,
2900                                      GSList *index_single_directories,
2901                                      GSList *ignored_directory_paths,
2902                                      GSList *ignored_directory_patterns)
2903 {
2904 	GSList *l;
2905 	gchar *basename;
2906 	gchar *path;
2907 	gboolean should_process;
2908 	gboolean is_hidden;
2909 
2910 	should_process = FALSE;
2911 	basename = NULL;
2912 
2913 	path = g_file_get_path (file);
2914 
2915 	/* First we check the GIO hidden check. This does a number of
2916 	 * things for us which is good (like checking ".foo" dirs).
2917 	 */
2918 	is_hidden = tracker_file_is_hidden (file);
2919 
2920 #ifdef __linux__
2921 	/* Second we check if the file is on FAT and if the hidden
2922 	 * attribute is set. GIO does this but ONLY on a Windows OS,
2923 	 * not for Windows files under a Linux OS, so we have to check
2924 	 * anyway.
2925 	 */
2926 	if (!is_hidden) {
2927 		int fd;
2928 
2929 		fd = g_open (path, O_RDONLY, 0);
2930 		if (fd != -1) {
2931 			__u32 attrs;
2932 
2933 			if (ioctl (fd, FAT_IOCTL_GET_ATTRIBUTES, &attrs) == 0) {
2934 				is_hidden = attrs & ATTR_HIDDEN ? TRUE : FALSE;
2935 			}
2936 
2937 			close (fd);
2938 		}
2939 	}
2940 #endif /* __linux__ */
2941 
2942 	if (is_hidden) {
2943 		/* FIXME: We need to check if the file is actually a
2944 		 * config specified location before blanket ignoring
2945 		 * all hidden files.
2946 		 */
2947 		if (tracker_string_in_gslist (path, index_recursive_directories)) {
2948 			should_process = TRUE;
2949 		}
2950 
2951 		if (tracker_string_in_gslist (path, index_single_directories)) {
2952 			should_process = TRUE;
2953 		}
2954 
2955 		/* Ignore hidden dirs */
2956 		goto done;
2957 	}
2958 
2959 	for (l = ignored_directory_paths; l; l = l->next) {
2960 		if (strcmp (l->data, path) == 0) {
2961 			goto done;
2962 		}
2963 	}
2964 
2965 	basename = g_file_get_basename (file);
2966 
2967 	for (l = ignored_directory_patterns; l; l = l->next) {
2968 		if (g_pattern_match_string (l->data, basename)) {
2969 			goto done;
2970 		}
2971 	}
2972 
2973 	/* Check module directory ignore patterns */
2974 	should_process = TRUE;
2975 
2976 done:
2977 	g_free (basename);
2978 	g_free (path);
2979 
2980 	return should_process;
2981 }
2982 
2983 gboolean
tracker_miner_files_check_directory_contents(GFile * parent,GList * children,GSList * ignored_content)2984 tracker_miner_files_check_directory_contents (GFile  *parent,
2985                                               GList  *children,
2986                                               GSList *ignored_content)
2987 {
2988 	GSList *l;
2989 
2990 	if (!ignored_content) {
2991 		return TRUE;
2992 	}
2993 
2994 	while (children) {
2995 		gchar *basename;
2996 
2997 		basename = g_file_get_basename (children->data);
2998 
2999 		for (l = ignored_content; l; l = l->next) {
3000 			if (g_strcmp0 (basename, l->data) == 0) {
3001 				gchar *parent_uri;
3002 
3003 				parent_uri = g_file_get_uri (parent);
3004 				/* g_debug ("Directory '%s' ignored since it contains a file named '%s'", */
3005 				/*          parent_uri, basename); */
3006 
3007 				g_free (parent_uri);
3008 				g_free (basename);
3009 
3010 				return FALSE;
3011 			}
3012 		}
3013 
3014 		children = children->next;
3015 		g_free (basename);
3016 	}
3017 
3018 	return TRUE;
3019 }
3020 
3021 gboolean
tracker_miner_files_monitor_directory(GFile * file,gboolean enable_monitors,GSList * directories_to_check)3022 tracker_miner_files_monitor_directory (GFile    *file,
3023                                        gboolean  enable_monitors,
3024                                        GSList   *directories_to_check)
3025 {
3026 	if (!enable_monitors) {
3027 		return FALSE;
3028 	}
3029 
3030 	/* We'll only get this signal for the directories where check_directory()
3031 	 * and check_directory_contents() returned TRUE, so by default we want
3032 	 * these directories to be indexed. */
3033 
3034 	return TRUE;
3035 }
3036 
3037 static void
remove_files_in_removable_media_cb(GObject * object,GAsyncResult * result,gpointer user_data)3038 remove_files_in_removable_media_cb (GObject      *object,
3039                                     GAsyncResult *result,
3040                                     gpointer      user_data)
3041 {
3042 	GError *error = NULL;
3043 
3044 	tracker_sparql_connection_update_finish (TRACKER_SPARQL_CONNECTION (object), result, &error);
3045 
3046 	if (error) {
3047 		g_critical ("Could not remove files in volumes: %s", error->message);
3048 		g_error_free (error);
3049 	}
3050 }
3051 
3052 static gboolean
miner_files_in_removable_media_remove_by_type(TrackerMinerFiles * miner,TrackerStorageType type)3053 miner_files_in_removable_media_remove_by_type (TrackerMinerFiles  *miner,
3054                                                TrackerStorageType  type)
3055 {
3056 	gboolean removable;
3057 	gboolean optical;
3058 
3059 	removable = TRACKER_STORAGE_TYPE_IS_REMOVABLE (type);
3060 	optical = TRACKER_STORAGE_TYPE_IS_OPTICAL (type);
3061 
3062 	/* Only remove if any of the flags was TRUE */
3063 	if (removable || optical) {
3064 		GString *queries;
3065 
3066 		g_debug ("  Removing all resources in store from %s ",
3067 		         optical ? "optical discs" : "removable devices");
3068 
3069 		queries = g_string_new ("");
3070 
3071 		/* Delete all resources where nie:dataSource is a volume
3072 		 * of the given type */
3073 		g_string_append_printf (queries,
3074 		                        "DELETE { "
3075 		                        "  ?f a rdfs:Resource . "
3076 		                        "  ?ie a rdfs:Resource "
3077 		                        "} WHERE { "
3078 		                        "  ?v a tracker:Volume ; "
3079 		                        "     tracker:isRemovable %s ; "
3080 		                        "     tracker:isOptical %s . "
3081 		                        "  ?f nie:dataSource ?v . "
3082 		                        "  ?ie nie:isStoredAs ?f "
3083 		                        "}",
3084 		                        removable ? "true" : "false",
3085 		                        optical ? "true" : "false");
3086 
3087 		tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
3088 		                                        queries->str,
3089 		                                        G_PRIORITY_LOW,
3090 		                                        NULL,
3091 		                                        remove_files_in_removable_media_cb,
3092 		                                        NULL);
3093 
3094 		g_string_free (queries, TRUE);
3095 
3096 		return TRUE;
3097 	}
3098 
3099 	return FALSE;
3100 }
3101 
3102 static void
miner_files_in_removable_media_remove_by_date(TrackerMinerFiles * miner,const gchar * date)3103 miner_files_in_removable_media_remove_by_date (TrackerMinerFiles  *miner,
3104                                                const gchar        *date)
3105 {
3106 	GString *queries;
3107 
3108 	g_debug ("  Removing all resources in store from removable or "
3109 	         "optical devices not mounted after '%s'",
3110 	         date);
3111 
3112 	queries = g_string_new ("");
3113 
3114 	/* Delete all resources where nie:dataSource is a volume
3115 	 * which was last unmounted before the given date */
3116 	g_string_append_printf (queries,
3117 	                        "DELETE { "
3118 	                        "  ?f a rdfs:Resource . "
3119 	                        "  ?ie a rdfs:Resource "
3120 	                        "} WHERE { "
3121 	                        "  ?v a tracker:Volume ; "
3122 	                        "     tracker:isRemovable true ; "
3123 	                        "     tracker:isMounted false ; "
3124 	                        "     tracker:unmountDate ?d . "
3125 	                        "  ?f nie:dataSource ?v . "
3126 	                        "  ?ie nie:isStoredAs ?f "
3127 	                        "  FILTER ( ?d < \"%s\") "
3128 	                        "}",
3129 	                        date);
3130 
3131 	tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (miner)),
3132 	                                        queries->str,
3133 	                                        G_PRIORITY_LOW,
3134 	                                        NULL,
3135 	                                        remove_files_in_removable_media_cb,
3136 	                                        NULL);
3137 
3138 	g_string_free (queries, TRUE);
3139 }
3140 
3141 static void
miner_files_add_removable_or_optical_directory(TrackerMinerFiles * mf,const gchar * mount_path,const gchar * uuid)3142 miner_files_add_removable_or_optical_directory (TrackerMinerFiles *mf,
3143                                                 const gchar       *mount_path,
3144                                                 const gchar       *uuid)
3145 {
3146 	TrackerIndexingTree *indexing_tree;
3147 	TrackerDirectoryFlags flags;
3148 	GFile *mount_point_file;
3149 
3150 	mount_point_file = g_file_new_for_path (mount_path);
3151 
3152 	/* UUID may be NULL, and if so, get it */
3153 	if (!uuid) {
3154 		uuid = tracker_storage_get_uuid_for_file (mf->private->storage,
3155 		                                          mount_point_file);
3156 		if (!uuid) {
3157 			g_critical ("Couldn't get UUID for mount point '%s'",
3158 			            mount_path);
3159 			g_object_unref (mount_point_file);
3160 			return;
3161 		}
3162 	}
3163 
3164 	indexing_tree = tracker_miner_fs_get_indexing_tree (TRACKER_MINER_FS (mf));
3165 	flags = TRACKER_DIRECTORY_FLAG_RECURSE |
3166 		TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
3167 		TRACKER_DIRECTORY_FLAG_PRESERVE |
3168 		TRACKER_DIRECTORY_FLAG_PRIORITY;
3169 
3170 	if (tracker_config_get_enable_monitors (mf->private->config)) {
3171 		flags |= TRACKER_DIRECTORY_FLAG_MONITOR;
3172 	}
3173 
3174 	g_object_set_qdata_full (G_OBJECT (mount_point_file),
3175 	                         mf->private->quark_mount_point_uuid,
3176 	                         g_strdup (uuid),
3177 	                         (GDestroyNotify) g_free);
3178 
3179 	g_message ("  Adding removable/optical: '%s'", mount_path);
3180 	tracker_indexing_tree_add (indexing_tree,
3181 				   mount_point_file,
3182 				   flags);
3183 	g_object_unref (mount_point_file);
3184 }
3185 
3186 gboolean
tracker_miner_files_is_file_eligible(TrackerMinerFiles * miner,GFile * file)3187 tracker_miner_files_is_file_eligible (TrackerMinerFiles *miner,
3188                                       GFile             *file)
3189 {
3190 	TrackerConfig *config;
3191 	GFile *dir;
3192 	GFileInfo *file_info;
3193 	gboolean is_dir;
3194 
3195 	file_info = g_file_query_info (file,
3196 	                               G_FILE_ATTRIBUTE_STANDARD_TYPE,
3197 	                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
3198 	                               NULL, NULL);
3199 
3200 	if (!file_info) {
3201 		/* file does not exist */
3202 		return FALSE;
3203 	}
3204 
3205 	is_dir = (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY);
3206 	g_object_unref (file_info);
3207 
3208 	g_object_get (miner,
3209 	              "config", &config,
3210 	              NULL);
3211 
3212 	if (is_dir) {
3213 		dir = g_object_ref (file);
3214 	} else {
3215 		if (!tracker_miner_files_check_file (file,
3216 		                                     tracker_config_get_ignored_file_paths (config),
3217 		                                     tracker_config_get_ignored_file_patterns (config))) {
3218 			/* file is not eligible to be indexed */
3219 			g_object_unref (config);
3220 			return FALSE;
3221 		}
3222 
3223 		dir = g_file_get_parent (file);
3224 	}
3225 
3226 	if (dir) {
3227 		gboolean found = FALSE;
3228 		GSList *l;
3229 
3230 		if (!tracker_miner_files_check_directory (dir,
3231 		                                          tracker_config_get_index_recursive_directories (config),
3232 		                                          tracker_config_get_index_single_directories (config),
3233 		                                          tracker_config_get_ignored_directory_paths (config),
3234 		                                          tracker_config_get_ignored_directory_patterns (config))) {
3235 			/* file is not eligible to be indexed */
3236 			g_object_unref (dir);
3237 			g_object_unref (config);
3238 			return FALSE;
3239 		}
3240 
3241 		l = tracker_config_get_index_recursive_directories (config);
3242 
3243 		while (l && !found) {
3244 			GFile *config_dir;
3245 
3246 			config_dir = g_file_new_for_path ((gchar *) l->data);
3247 
3248 			if (g_file_equal (dir, config_dir) ||
3249 			    g_file_has_prefix (dir, config_dir)) {
3250 				found = TRUE;
3251 			}
3252 
3253 			g_object_unref (config_dir);
3254 			l = l->next;
3255 		}
3256 
3257 		l = tracker_config_get_index_single_directories (config);
3258 
3259 		while (l && !found) {
3260 			GFile *config_dir;
3261 
3262 			config_dir = g_file_new_for_path ((gchar *) l->data);
3263 
3264 			if (g_file_equal (dir, config_dir)) {
3265 				found = TRUE;
3266 			}
3267 
3268 			g_object_unref (config_dir);
3269 			l = l->next;
3270 		}
3271 
3272 		g_object_unref (dir);
3273 
3274 		if (!found) {
3275 			/* file is not eligible to be indexed */
3276 			g_object_unref (config);
3277 			return FALSE;
3278 		}
3279 	}
3280 
3281 	g_object_unref (config);
3282 
3283 	/* file is eligible to be indexed */
3284 	return TRUE;
3285 }
3286 
3287 inline static gchar *
get_first_index_filename(void)3288 get_first_index_filename (void)
3289 {
3290 	return g_build_filename (g_get_user_cache_dir (),
3291 	                         "tracker",
3292 	                         FIRST_INDEX_FILENAME,
3293 	                         NULL);
3294 }
3295 
3296 /**
3297  * tracker_miner_files_get_first_index_done:
3298  *
3299  * Check if first full index of files was already done.
3300  *
3301  * Returns: %TRUE if a first full index have been done, %FALSE otherwise.
3302  **/
3303 gboolean
tracker_miner_files_get_first_index_done(void)3304 tracker_miner_files_get_first_index_done (void)
3305 {
3306 	gboolean exists;
3307 	gchar *filename;
3308 
3309 	filename = get_first_index_filename ();
3310 	exists = g_file_test (filename, G_FILE_TEST_EXISTS);
3311 	g_free (filename);
3312 
3313 	return exists;
3314 }
3315 
3316 /**
3317  * tracker_miner_files_set_first_index_done:
3318  *
3319  * Set the status of the first full index of files. Should be set to
3320  *  %FALSE if the index was never done or if a reindex is needed. When
3321  *  the index is completed, should be set to %TRUE.
3322  **/
3323 void
tracker_miner_files_set_first_index_done(gboolean done)3324 tracker_miner_files_set_first_index_done (gboolean done)
3325 {
3326 	gboolean already_exists;
3327 	gchar *filename;
3328 
3329 	filename = get_first_index_filename ();
3330 	already_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
3331 
3332 	if (done && !already_exists) {
3333 		GError *error = NULL;
3334 
3335 		/* If done, create stamp file if not already there */
3336 		if (!g_file_set_contents (filename, PACKAGE_VERSION, -1, &error)) {
3337 			g_warning ("  Could not create file:'%s' failed, %s",
3338 			           filename,
3339 			           error->message);
3340 			g_error_free (error);
3341 		} else {
3342 			g_info ("  First index file:'%s' created", filename);
3343 		}
3344 	} else if (!done && already_exists) {
3345 		/* If NOT done, remove stamp file */
3346 		g_info ("  Removing first index file:'%s'", filename);
3347 
3348 		if (g_remove (filename)) {
3349 			g_warning ("    Could not remove file:'%s': %m",
3350 			           filename);
3351 		}
3352 	}
3353 
3354 	g_free (filename);
3355 }
3356 
3357 static inline gchar *
get_last_crawl_filename(void)3358 get_last_crawl_filename (void)
3359 {
3360 	return g_build_filename (g_get_user_cache_dir (),
3361 	                         "tracker",
3362 	                         LAST_CRAWL_FILENAME,
3363 	                         NULL);
3364 }
3365 
3366 /**
3367  * tracker_miner_files_get_last_crawl_done:
3368  *
3369  * Check when last crawl was performed.
3370  *
3371  * Returns: time_t() value when last crawl occurred, otherwise 0.
3372  **/
3373 guint64
tracker_miner_files_get_last_crawl_done(void)3374 tracker_miner_files_get_last_crawl_done (void)
3375 {
3376 	gchar *filename;
3377 	gchar *content;
3378 	guint64 then;
3379 
3380 	filename = get_last_crawl_filename ();
3381 
3382 	if (!g_file_get_contents (filename, &content, NULL, NULL)) {
3383 		g_info ("  No previous timestamp, crawling forced");
3384 		return 0;
3385 	}
3386 
3387 	then = g_ascii_strtoull (content, NULL, 10);
3388 	g_free (content);
3389 
3390 	return then;
3391 }
3392 
3393 /**
3394  * tracker_miner_files_set_last_crawl_done:
3395  *
3396  * Set the status of the first full index of files. Should be set to
3397  *  %FALSE if the index was never done or if a reindex is needed. When
3398  *  the index is completed, should be set to %TRUE.
3399  **/
3400 void
tracker_miner_files_set_last_crawl_done(gboolean done)3401 tracker_miner_files_set_last_crawl_done (gboolean done)
3402 {
3403 	gboolean already_exists;
3404 	gchar *filename;
3405 
3406 	filename = get_last_crawl_filename ();
3407 	already_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
3408 
3409 	if (done && !already_exists) {
3410 		GError *error = NULL;
3411 		gchar *content;
3412 
3413 		content = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64) time (NULL));
3414 
3415 		/* If done, create stamp file if not already there */
3416 		if (!g_file_set_contents (filename, content, -1, &error)) {
3417 			g_warning ("  Could not create file:'%s' failed, %s",
3418 			           filename,
3419 			           error->message);
3420 			g_error_free (error);
3421 		} else {
3422 			g_info ("  Last crawl file:'%s' created", filename);
3423 		}
3424 
3425 		g_free (content);
3426 	} else if (!done && already_exists) {
3427 		/* If NOT done, remove stamp file */
3428 		g_info ("  Removing last crawl file:'%s'", filename);
3429 
3430 		if (g_remove (filename)) {
3431 			g_warning ("    Could not remove file:'%s': %m",
3432 			           filename);
3433 		}
3434 	}
3435 
3436 	g_free (filename);
3437 }
3438 
3439 inline static gchar *
get_need_mtime_check_filename(void)3440 get_need_mtime_check_filename (void)
3441 {
3442 	return g_build_filename (g_get_user_cache_dir (),
3443 	                         "tracker",
3444 	                         NEED_MTIME_CHECK_FILENAME,
3445 	                         NULL);
3446 }
3447 
3448 /**
3449  * tracker_miner_files_get_need_mtime_check:
3450  *
3451  * Check if the miner-fs was cleanly shutdown or not.
3452  *
3453  * Returns: %TRUE if we need to check mtimes for directories against
3454  * the database on the next start for the miner-fs, %FALSE otherwise.
3455  **/
3456 gboolean
tracker_miner_files_get_need_mtime_check(void)3457 tracker_miner_files_get_need_mtime_check (void)
3458 {
3459 	gboolean exists;
3460 	gchar *filename;
3461 
3462 	filename = get_need_mtime_check_filename ();
3463 	exists = g_file_test (filename, G_FILE_TEST_EXISTS);
3464 	g_free (filename);
3465 
3466 	/* Existence of the file means we cleanly shutdown before and
3467 	 * don't need to do the mtime check again on this start.
3468 	 */
3469 	return !exists;
3470 }
3471 
3472 /**
3473  * tracker_miner_files_set_need_mtime_check:
3474  * @needed: a #gboolean
3475  *
3476  * If the next start of miner-fs should perform a full mtime check
3477  * against each directory found and those in the database (for
3478  * complete synchronisation), then @needed should be #TRUE, otherwise
3479  * #FALSE.
3480  *
3481  * Creates a file in $HOME/.cache/tracker/ if an mtime check is not
3482  * needed. The idea behind this is that a check is forced if the file
3483  * is not cleaned up properly on shutdown (i.e. due to a crash or any
3484  * other uncontrolled shutdown reason).
3485  **/
3486 void
tracker_miner_files_set_need_mtime_check(gboolean needed)3487 tracker_miner_files_set_need_mtime_check (gboolean needed)
3488 {
3489 	gboolean already_exists;
3490 	gchar *filename;
3491 
3492 	filename = get_need_mtime_check_filename ();
3493 	already_exists = g_file_test (filename, G_FILE_TEST_EXISTS);
3494 
3495 	/* !needed = add file
3496 	 *  needed = remove file
3497 	 */
3498 	if (!needed && !already_exists) {
3499 		GError *error = NULL;
3500 
3501 		/* Create stamp file if not already there */
3502 		if (!g_file_set_contents (filename, PACKAGE_VERSION, -1, &error)) {
3503 			g_warning ("  Could not create file:'%s' failed, %s",
3504 			           filename,
3505 			           error->message);
3506 			g_error_free (error);
3507 		} else {
3508 			g_info ("  Need mtime check file:'%s' created", filename);
3509 		}
3510 	} else if (needed && already_exists) {
3511 		/* Remove stamp file */
3512 		g_info ("  Removing need mtime check file:'%s'", filename);
3513 
3514 		if (g_remove (filename)) {
3515 			g_warning ("    Could not remove file:'%s': %m",
3516 			           filename);
3517 		}
3518 	}
3519 
3520 	g_free (filename);
3521 }
3522 
3523 void
tracker_miner_files_set_mtime_checking(TrackerMinerFiles * mf,gboolean mtime_check)3524 tracker_miner_files_set_mtime_checking (TrackerMinerFiles *mf,
3525                                         gboolean           mtime_check)
3526 {
3527 	mf->private->mtime_check = mtime_check;
3528 }
3529 
3530 void
tracker_miner_files_writeback_file(TrackerMinerFiles * mf,GFile * file,GStrv rdf_types,GPtrArray * results)3531 tracker_miner_files_writeback_file (TrackerMinerFiles *mf,
3532                                     GFile             *file,
3533                                     GStrv              rdf_types,
3534                                     GPtrArray         *results)
3535 {
3536 	GCancellable *cancellable;
3537 
3538 	if (!g_hash_table_contains (mf->private->writeback_tasks, file)) {
3539 		cancellable = g_cancellable_new ();
3540 		g_hash_table_insert (mf->private->writeback_tasks, g_object_ref (file), cancellable);
3541 		sync_writeback_pause_state (mf);
3542 		g_signal_emit (mf, signals[WRITEBACK], 0, file, rdf_types,
3543 		               results, cancellable);
3544 	}
3545 }
3546 
3547 /**
3548  * tracker_miner_files_writeback_notify:
3549  * @fs: a #TrackerMinerFS
3550  * @file: a #GFile
3551  * @error: a #GError with the error that happened during processing, or %NULL.
3552  *
3553  * Notifies @fs that all writing back on @file has been finished, if any error
3554  * happened during file data processing, it should be passed in @error, else
3555  * that parameter will contain %NULL to reflect success.
3556  **/
3557 void
tracker_miner_files_writeback_notify(TrackerMinerFiles * mf,GFile * file,const GError * error)3558 tracker_miner_files_writeback_notify (TrackerMinerFiles *mf,
3559                                       GFile             *file,
3560                                       const GError      *error)
3561 {
3562 	GCancellable *cancellable;
3563 
3564 	g_return_if_fail (TRACKER_IS_MINER_FILES (mf));
3565 	g_return_if_fail (G_IS_FILE (file));
3566 
3567 	cancellable = g_hash_table_lookup (mf->private->writeback_tasks, file);
3568 
3569 	if (!cancellable)
3570 		return;
3571 
3572 	if (error) {
3573 		gchar *uri = g_file_get_uri (file);
3574 		g_warning ("Writeback on %s got error: %s\n",
3575 		           uri, error->message);
3576 		g_free (uri);
3577 	}
3578 
3579 	g_hash_table_steal (mf->private->writeback_tasks, file);
3580 	g_object_unref (cancellable);
3581 }
3582