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