tracker-0.16.2/src/libtracker-miner/tracker-miner-fs.c

No issues found

Incomplete coverage

Tool Failure ID Location Function Message Data
clang-analyzer no-output-found tracker-miner-fs.c Message(text='Unable to locate XML output from invoke-clang-analyzer') None
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
   1 /*
   2  * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  *
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the
  16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17  * Boston, MA  02110-1301, USA.
  18  */
  19 
  20 #include "config.h"
  21 
  22 #include <libtracker-common/tracker-date-time.h>
  23 #include <libtracker-common/tracker-dbus.h>
  24 #include <libtracker-common/tracker-file-utils.h>
  25 #include <libtracker-common/tracker-log.h>
  26 #include <libtracker-common/tracker-utils.h>
  27 
  28 #include "tracker-crawler.h"
  29 #include "tracker-marshal.h"
  30 #include "tracker-miner-fs.h"
  31 #include "tracker-media-art.h"
  32 #include "tracker-monitor.h"
  33 #include "tracker-utils.h"
  34 #include "tracker-thumbnailer.h"
  35 #include "tracker-priority-queue.h"
  36 #include "tracker-task-pool.h"
  37 #include "tracker-sparql-buffer.h"
  38 #include "tracker-file-notifier.h"
  39 
  40 /* If defined will print the tree from GNode while running */
  41 #ifdef CRAWLED_TREE_ENABLE_TRACE
  42 #warning Tree debugging traces enabled
  43 #endif /* CRAWLED_TREE_ENABLE_TRACE */
  44 
  45 /* If defined will print push/pop actions on queues */
  46 #ifdef EVENT_QUEUE_ENABLE_TRACE
  47 #warning Event Queue traces enabled
  48 #define EVENT_QUEUE_LOG_PREFIX "[Event Queues] "
  49 #define EVENT_QUEUE_STATUS_TIMEOUT_SECS 30
  50 #define trace_eq(message, ...) g_debug (EVENT_QUEUE_LOG_PREFIX message, ##__VA_ARGS__)
  51 #define trace_eq_action(pushed, queue_name, position, gfile1, gfile2, reason) \
  52 	do { \
  53 		gchar *uri1 = g_file_get_uri (gfile1); \
  54 		gchar *uri2 = gfile2 ? g_file_get_uri (gfile2) : NULL; \
  55 		g_debug ("%s%s '%s%s%s' %s %s of queue '%s'%s%s", \
  56 		         EVENT_QUEUE_LOG_PREFIX, \
  57 		         pushed ? "Pushed" : "Popped", \
  58 		         uri1, \
  59 		         uri2 ? "->" : "", \
  60 		         uri2 ? uri2 : "", \
  61 		         pushed ? "to" : "from", \
  62 		         position, \
  63 		         queue_name, \
  64 		         reason ? ": " : "", \
  65 		         reason ? reason : ""); \
  66 		g_free (uri1); \
  67 		g_free (uri2); \
  68 	} while (0)
  69 #define trace_eq_push_tail(queue_name, gfile, reason)	  \
  70 	trace_eq_action (TRUE, queue_name, "tail", gfile, NULL, reason)
  71 #define trace_eq_push_head(queue_name, gfile, reason)	  \
  72 	trace_eq_action (TRUE, queue_name, "head", gfile, NULL, reason)
  73 #define trace_eq_push_tail_2(queue_name, gfile1, gfile2, reason)	  \
  74 	trace_eq_action (TRUE, queue_name, "tail", gfile1, gfile2, reason)
  75 #define trace_eq_push_head_2(queue_name, gfile1, gfile2, reason)	  \
  76 	trace_eq_action (TRUE, queue_name, "head", gfile1, gfile2, reason)
  77 #define trace_eq_pop_head(queue_name, gfile)	  \
  78 	trace_eq_action (FALSE, queue_name, "head", gfile, NULL, NULL)
  79 #define trace_eq_pop_head_2(queue_name, gfile1, gfile2)	  \
  80 	trace_eq_action (FALSE, queue_name, "head", gfile1, gfile2, NULL)
  81 static gboolean miner_fs_queues_status_trace_timeout_cb (gpointer data);
  82 #else
  83 #define trace_eq(...)
  84 #define trace_eq_push_tail(...)
  85 #define trace_eq_push_head(...)
  86 #define trace_eq_push_tail_2(...)
  87 #define trace_eq_push_head_2(...)
  88 #define trace_eq_pop_head(...)
  89 #define trace_eq_pop_head_2(...)
  90 #endif /* EVENT_QUEUE_ENABLE_TRACE */
  91 
  92 /* Number of times a GFile can be re-queued before it's dropped for
  93  * whatever reason to avoid infinite loops.
  94 */
  95 #define REENTRY_MAX 2
  96 
  97 /* Default processing pool limits to be set */
  98 #define DEFAULT_WAIT_POOL_LIMIT 1
  99 #define DEFAULT_READY_POOL_LIMIT 1
 100 
 101 /* Put tasks processing at a lower priority so other events
 102  * (timeouts, monitor events, etc...) are guaranteed to be
 103  * dispatched promptly.
 104  */
 105 #define TRACKER_TASK_PRIORITY G_PRIORITY_DEFAULT_IDLE + 10
 106 
 107 /**
 108  * SECTION:tracker-miner-fs
 109  * @short_description: Abstract base class for filesystem miners
 110  * @include: libtracker-miner/tracker-miner.h
 111  *
 112  * #TrackerMinerFS is an abstract base class for miners that collect data
 113  * from the filesystem, all the filesystem crawling and monitoring is
 114  * abstracted away, leaving to implementations the decisions of what
 115  * directories/files should it process, and the actual data extraction.
 116  **/
 117 
 118 #define TRACKER_MINER_FS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_MINER_FS, TrackerMinerFSPrivate))
 119 
 120 typedef struct {
 121 	GFile *file;
 122 	GFile *source_file;
 123 } ItemMovedData;
 124 
 125 typedef struct {
 126 	GFile     *file;
 127 	GPtrArray *results;
 128 	GStrv      rdf_types;
 129 	GCancellable *cancellable;
 130 	guint notified : 1;
 131 } ItemWritebackData;
 132 
 133 typedef struct {
 134 	GFile *file;
 135 	gchar *urn;
 136 	gchar *parent_urn;
 137 	gint priority;
 138 	GCancellable *cancellable;
 139 	TrackerSparqlBuilder *builder;
 140 	TrackerMiner *miner;
 141 } UpdateProcessingTaskContext;
 142 
 143 typedef struct {
 144 	GMainLoop *main_loop;
 145 	GString   *sparql;
 146 	const gchar *source_uri;
 147 	const gchar *uri;
 148 } RecursiveMoveData;
 149 
 150 struct _TrackerMinerFSPrivate {
 151 	/* File queues for indexer */
 152 	TrackerPriorityQueue *items_created;
 153 	TrackerPriorityQueue *items_updated;
 154 	TrackerPriorityQueue *items_deleted;
 155 	TrackerPriorityQueue *items_moved;
 156 	TrackerPriorityQueue *items_writeback;
 157 #ifdef EVENT_QUEUE_ENABLE_TRACE
 158 	guint           queue_status_timeout_id;
 159 #endif /* EVENT_QUEUE_ENABLE_TRACE */
 160 
 161 	TrackerFileNotifier *file_notifier;
 162 
 163 	GHashTable     *items_ignore_next_update;
 164 
 165 	GQuark          quark_ignore_file;
 166 	GQuark          quark_attribute_updated;
 167 	GQuark          quark_directory_found_crawling;
 168 	GQuark          quark_reentry_counter;
 169 
 170 	GTimer         *timer;
 171 	GTimer         *extraction_timer;
 172 
 173 	guint           item_queues_handler_id;
 174 	GFile          *item_queue_blocker;
 175 
 176 	gdouble         throttle;
 177 
 178 	/* Extraction tasks */
 179 	TrackerTaskPool *task_pool;
 180 
 181 	/* Writeback tasks */
 182 	TrackerTaskPool *writeback_pool;
 183 
 184 	/* Sparql insertion tasks */
 185 	TrackerSparqlBuffer *sparql_buffer;
 186 	guint sparql_buffer_limit;
 187 
 188 	TrackerIndexingTree *indexing_tree;
 189 
 190 	/* Status */
 191 	guint           been_started : 1;     /* TRUE if miner has been started */
 192 	guint           been_crawled : 1;     /* TRUE if initial crawling has been
 193 	                                       * done */
 194 	guint           shown_totals : 1;     /* TRUE if totals have been shown */
 195 	guint           is_paused : 1;        /* TRUE if miner is paused */
 196 	guint           mtime_checking : 1;   /* TRUE if mtime checks should be done
 197 	                                       * during initial crawling. */
 198 	guint           initial_crawling : 1; /* TRUE if initial crawling should be
 199 	                                       * done */
 200 	guint           timer_stopped : 1;    /* TRUE if main timer is stopped */
 201 	guint           extraction_timer_stopped : 1; /* TRUE if the extraction
 202 						       * timer is stopped */
 203 
 204 	/* Statistics */
 205 	guint           total_directories_found;
 206 	guint           total_directories_ignored;
 207 	guint           total_files_found;
 208 	guint           total_files_ignored;
 209 
 210 	guint           directories_found;
 211 	guint           directories_ignored;
 212 	guint           files_found;
 213 	guint           files_ignored;
 214 
 215 	guint           total_files_processed;
 216 	guint           total_files_notified;
 217 	guint           total_files_notified_error;
 218 };
 219 
 220 typedef enum {
 221 	QUEUE_NONE,
 222 	QUEUE_CREATED,
 223 	QUEUE_UPDATED,
 224 	QUEUE_DELETED,
 225 	QUEUE_MOVED,
 226 	QUEUE_IGNORE_NEXT_UPDATE,
 227 	QUEUE_WAIT,
 228 	QUEUE_WRITEBACK
 229 } QueueState;
 230 
 231 enum {
 232 	PROCESS_FILE,
 233 	PROCESS_FILE_ATTRIBUTES,
 234 	IGNORE_NEXT_UPDATE_FILE,
 235 	FINISHED,
 236 	WRITEBACK_FILE,
 237 	LAST_SIGNAL
 238 };
 239 
 240 enum {
 241 	PROP_0,
 242 	PROP_THROTTLE,
 243 	PROP_WAIT_POOL_LIMIT,
 244 	PROP_READY_POOL_LIMIT,
 245 	PROP_MTIME_CHECKING,
 246 	PROP_INITIAL_CRAWLING
 247 };
 248 
 249 static void           miner_fs_initable_iface_init        (GInitableIface       *iface);
 250 
 251 static void           fs_finalize                         (GObject              *object);
 252 static void           fs_set_property                     (GObject              *object,
 253                                                            guint                 prop_id,
 254                                                            const GValue         *value,
 255                                                            GParamSpec           *pspec);
 256 static void           fs_get_property                     (GObject              *object,
 257                                                            guint                 prop_id,
 258                                                            GValue               *value,
 259                                                            GParamSpec           *pspec);
 260 static void           miner_started                       (TrackerMiner         *miner);
 261 static void           miner_stopped                       (TrackerMiner         *miner);
 262 static void           miner_paused                        (TrackerMiner         *miner);
 263 static void           miner_resumed                       (TrackerMiner         *miner);
 264 static void           miner_ignore_next_update            (TrackerMiner         *miner,
 265                                                            const GStrv           subjects);
 266 static ItemMovedData *item_moved_data_new                 (GFile                *file,
 267                                                            GFile                *source_file);
 268 static void           item_moved_data_free                (ItemMovedData        *data);
 269 static void           item_writeback_data_free            (ItemWritebackData    *data);
 270 
 271 static void           indexing_tree_directory_removed     (TrackerIndexingTree  *indexing_tree,
 272                                                            GFile                *directory,
 273                                                            gpointer              user_data);
 274 static void           file_notifier_file_created          (TrackerFileNotifier  *notifier,
 275                                                            GFile                *file,
 276                                                            gpointer              user_data);
 277 static void           file_notifier_file_deleted          (TrackerFileNotifier  *notifier,
 278                                                            GFile                *file,
 279                                                            gpointer              user_data);
 280 static void           file_notifier_file_updated          (TrackerFileNotifier  *notifier,
 281                                                            GFile                *file,
 282                                                            gboolean              attributes_only,
 283                                                            gpointer              user_data);
 284 static void           file_notifier_file_moved            (TrackerFileNotifier  *notifier,
 285                                                            GFile                *source,
 286                                                            GFile                *dest,
 287                                                            gpointer              user_data);
 288 static void           file_notifier_directory_started     (TrackerFileNotifier *notifier,
 289                                                            GFile               *directory,
 290                                                            gpointer             user_data);
 291 static void           file_notifier_directory_finished    (TrackerFileNotifier *notifier,
 292                                                            GFile               *directory,
 293                                                            guint                directories_found,
 294                                                            guint                directories_ignored,
 295                                                            guint                files_found,
 296                                                            guint                files_ignored,
 297                                                            gpointer             user_data);
 298 static void           file_notifier_finished              (TrackerFileNotifier *notifier,
 299                                                            gpointer             user_data);
 300 
 301 static void           item_queue_handlers_set_up          (TrackerMinerFS       *fs);
 302 static void           item_update_children_uri            (TrackerMinerFS       *fs,
 303                                                            RecursiveMoveData    *data,
 304                                                            const gchar          *source_uri,
 305                                                            const gchar          *uri);
 306 
 307 static void           task_pool_cancel_foreach                (gpointer        data,
 308                                                                gpointer        user_data);
 309 static void           task_pool_limit_reached_notify_cb       (GObject        *object,
 310                                                                GParamSpec     *pspec,
 311                                                                gpointer        user_data);
 312 
 313 static GInitableIface* miner_fs_initable_parent_iface;
 314 static guint signals[LAST_SIGNAL] = { 0, };
 315 
 316 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (TrackerMinerFS, tracker_miner_fs, TRACKER_TYPE_MINER,
 317                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
 318                                                          miner_fs_initable_iface_init));
 319 
 320 static void
 321 tracker_miner_fs_class_init (TrackerMinerFSClass *klass)
 322 {
 323 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 324 	TrackerMinerClass *miner_class = TRACKER_MINER_CLASS (klass);
 325 
 326 	object_class->finalize = fs_finalize;
 327 	object_class->set_property = fs_set_property;
 328 	object_class->get_property = fs_get_property;
 329 
 330 	miner_class->started = miner_started;
 331 	miner_class->stopped = miner_stopped;
 332 	miner_class->paused  = miner_paused;
 333 	miner_class->resumed = miner_resumed;
 334 	miner_class->ignore_next_update = miner_ignore_next_update;
 335 
 336 	g_object_class_install_property (object_class,
 337 	                                 PROP_THROTTLE,
 338 	                                 g_param_spec_double ("throttle",
 339 	                                                      "Throttle",
 340 	                                                      "Modifier for the indexing speed, 0 is max speed",
 341 	                                                      0, 1, 0,
 342 	                                                      G_PARAM_READWRITE));
 343 	g_object_class_install_property (object_class,
 344 	                                 PROP_WAIT_POOL_LIMIT,
 345 	                                 g_param_spec_uint ("processing-pool-wait-limit",
 346 	                                                    "Processing pool limit for WAIT tasks",
 347 	                                                    "Maximum number of files that can be concurrently "
 348 	                                                    "processed by the upper layer",
 349 	                                                    1, G_MAXUINT, DEFAULT_WAIT_POOL_LIMIT,
 350 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 351 	g_object_class_install_property (object_class,
 352 	                                 PROP_READY_POOL_LIMIT,
 353 	                                 g_param_spec_uint ("processing-pool-ready-limit",
 354 	                                                    "Processing pool limit for READY tasks",
 355 	                                                    "Maximum number of SPARQL updates that can be merged "
 356 	                                                    "in a single connection to the store",
 357 	                                                    1, G_MAXUINT, DEFAULT_READY_POOL_LIMIT,
 358 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 359 	g_object_class_install_property (object_class,
 360 	                                 PROP_MTIME_CHECKING,
 361 	                                 g_param_spec_boolean ("mtime-checking",
 362 	                                                       "Mtime checking",
 363 	                                                       "Whether to perform mtime checks during initial crawling or not",
 364 	                                                       TRUE,
 365 	                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 366 	g_object_class_install_property (object_class,
 367 	                                 PROP_INITIAL_CRAWLING,
 368 	                                 g_param_spec_boolean ("initial-crawling",
 369 	                                                       "Initial crawling",
 370 	                                                       "Whether to perform initial crawling or not",
 371 	                                                       TRUE,
 372 	                                                       G_PARAM_READWRITE));
 373 
 374 	/**
 375 	 * TrackerMinerFS::process-file:
 376 	 * @miner_fs: the #TrackerMinerFS
 377 	 * @file: a #GFile
 378 	 * @builder: a #TrackerSparqlBuilder
 379 	 * @cancellable: a #GCancellable
 380 	 *
 381 	 * The ::process-file signal is emitted whenever a file should
 382 	 * be processed, and it's metadata extracted.
 383 	 *
 384 	 * @builder is the #TrackerSparqlBuilder where all sparql updates
 385 	 * to be performed for @file will be appended.
 386 	 *
 387 	 * This signal allows both synchronous and asynchronous extraction,
 388 	 * in the synchronous case @cancellable can be safely ignored. In
 389 	 * either case, on successful metadata extraction, implementations
 390 	 * must call tracker_miner_fs_file_notify() to indicate that
 391 	 * processing has finished on @file, so the miner can execute
 392 	 * the SPARQL updates and continue processing other files.
 393 	 *
 394 	 * Returns: %TRUE if the file is accepted for processing,
 395 	 *          %FALSE if the file should be ignored.
 396 	 *
 397 	 * Since: 0.8
 398 	 **/
 399 	signals[PROCESS_FILE] =
 400 		g_signal_new ("process-file",
 401 		              G_OBJECT_CLASS_TYPE (object_class),
 402 		              G_SIGNAL_RUN_LAST,
 403 		              G_STRUCT_OFFSET (TrackerMinerFSClass, process_file),
 404 		              NULL, NULL,
 405 		              tracker_marshal_BOOLEAN__OBJECT_OBJECT_OBJECT,
 406 		              G_TYPE_BOOLEAN,
 407 		              3, G_TYPE_FILE, TRACKER_SPARQL_TYPE_BUILDER, G_TYPE_CANCELLABLE);
 408 
 409 	/**
 410 	 * TrackerMinerFS::process-file-attributes:
 411 	 * @miner_fs: the #TrackerMinerFS
 412 	 * @file: a #GFile
 413 	 * @builder: a #TrackerSparqlBuilder
 414 	 * @cancellable: a #GCancellable
 415 	 *
 416 	 * The ::process-file-attributes signal is emitted whenever a file should
 417 	 * be processed, but only the attribute-related metadata extracted.
 418 	 *
 419 	 * @builder is the #TrackerSparqlBuilder where all sparql updates
 420 	 * to be performed for @file will be appended. For the properties being
 421 	 * updated, the DELETE statements should be included as well.
 422 	 *
 423 	 * This signal allows both synchronous and asynchronous extraction,
 424 	 * in the synchronous case @cancellable can be safely ignored. In
 425 	 * either case, on successful metadata extraction, implementations
 426 	 * must call tracker_miner_fs_file_notify() to indicate that
 427 	 * processing has finished on @file, so the miner can execute
 428 	 * the SPARQL updates and continue processing other files.
 429 	 *
 430 	 * Returns: %TRUE if the file is accepted for processing,
 431 	 *          %FALSE if the file should be ignored.
 432 	 *
 433 	 * Since: 0.10
 434 	 **/
 435 	signals[PROCESS_FILE_ATTRIBUTES] =
 436 		g_signal_new ("process-file-attributes",
 437 		              G_OBJECT_CLASS_TYPE (object_class),
 438 		              G_SIGNAL_RUN_LAST,
 439 		              G_STRUCT_OFFSET (TrackerMinerFSClass, process_file_attributes),
 440 		              NULL, NULL,
 441 		              tracker_marshal_BOOLEAN__OBJECT_OBJECT_OBJECT,
 442 		              G_TYPE_BOOLEAN,
 443 		              3, G_TYPE_FILE, TRACKER_SPARQL_TYPE_BUILDER, G_TYPE_CANCELLABLE);
 444 
 445 	/**
 446 	 * TrackerMinerFS::ignore-next-update-file:
 447 	 * @miner_fs: the #TrackerMinerFS
 448 	 * @file: a #GFile
 449 	 * @builder: a #TrackerSparqlBuilder
 450 	 * @cancellable: a #GCancellable
 451 	 *
 452 	 * The ::ignore-next-update-file signal is emitted whenever a file should
 453 	 * be marked as to ignore on next update, and it's metadata prepared for that.
 454 	 *
 455 	 * @builder is the #TrackerSparqlBuilder where all sparql updates
 456 	 * to be performed for @file will be appended.
 457 	 *
 458 	 * Returns: %TRUE on success
 459 	 *          %FALSE on failure
 460 	 *
 461 	 * Since: 0.8
 462 	 *
 463 	 * Deprecated: 0.12
 464 	 **/
 465 	signals[IGNORE_NEXT_UPDATE_FILE] =
 466 		g_signal_new ("ignore-next-update-file",
 467 		              G_OBJECT_CLASS_TYPE (object_class),
 468 		              G_SIGNAL_RUN_LAST,
 469 		              G_STRUCT_OFFSET (TrackerMinerFSClass, ignore_next_update_file),
 470 		              NULL, NULL,
 471 		              tracker_marshal_BOOLEAN__OBJECT_OBJECT_OBJECT,
 472 		              G_TYPE_BOOLEAN,
 473 		              3, G_TYPE_FILE, TRACKER_SPARQL_TYPE_BUILDER, G_TYPE_CANCELLABLE);
 474 
 475 	/**
 476 	 * TrackerMinerFS::finished:
 477 	 * @miner_fs: the #TrackerMinerFS
 478 	 * @elapsed: elapsed time since mining was started
 479 	 * @directories_found: number of directories found
 480 	 * @directories_ignored: number of ignored directories
 481 	 * @files_found: number of files found
 482 	 * @files_ignored: number of ignored files
 483 	 *
 484 	 * The ::finished signal is emitted when @miner_fs has finished
 485 	 * all pending processing.
 486 	 *
 487 	 * Since: 0.8
 488 	 **/
 489 	signals[FINISHED] =
 490 		g_signal_new ("finished",
 491 		              G_TYPE_FROM_CLASS (object_class),
 492 		              G_SIGNAL_RUN_LAST,
 493 		              G_STRUCT_OFFSET (TrackerMinerFSClass, finished),
 494 		              NULL, NULL,
 495 		              tracker_marshal_VOID__DOUBLE_UINT_UINT_UINT_UINT,
 496 		              G_TYPE_NONE,
 497 		              5,
 498 		              G_TYPE_DOUBLE,
 499 		              G_TYPE_UINT,
 500 		              G_TYPE_UINT,
 501 		              G_TYPE_UINT,
 502 		              G_TYPE_UINT);
 503 
 504 	/**
 505 	 * TrackerMinerFS::writeback-file:
 506 	 * @miner_fs: the #TrackerMinerFS
 507 	 * @file: a #GFile
 508 	 * @rdf_types: the set of RDF types
 509 	 * @results: (element-type GStrv): a set of results prepared by the preparation query
 510 	 * @cancellable: a #GCancellable
 511 	 *
 512 	 * The ::writeback-file signal is emitted whenever a file must be written
 513 	 * back
 514 	 *
 515 	 * Returns: %TRUE on success, %FALSE otherwise
 516 	 *
 517 	 * Since: 0.10.20
 518 	 **/
 519 	signals[WRITEBACK_FILE] =
 520 		g_signal_new ("writeback-file",
 521 		              G_OBJECT_CLASS_TYPE (object_class),
 522 		              G_SIGNAL_RUN_LAST,
 523 		              G_STRUCT_OFFSET (TrackerMinerFSClass, writeback_file),
 524 		              NULL,
 525 		              NULL,
 526 		              tracker_marshal_BOOLEAN__OBJECT_BOXED_BOXED_OBJECT,
 527 		              G_TYPE_BOOLEAN,
 528 		              4,
 529 		              G_TYPE_FILE,
 530 		              G_TYPE_STRV,
 531 		              G_TYPE_PTR_ARRAY,
 532 		              G_TYPE_CANCELLABLE);
 533 
 534 	g_type_class_add_private (object_class, sizeof (TrackerMinerFSPrivate));
 535 }
 536 
 537 static void
 538 tracker_miner_fs_init (TrackerMinerFS *object)
 539 {
 540 	TrackerMinerFSPrivate *priv;
 541 
 542 	object->priv = TRACKER_MINER_FS_GET_PRIVATE (object);
 543 
 544 	priv = object->priv;
 545 
 546 	priv->timer = g_timer_new ();
 547 	priv->extraction_timer = g_timer_new ();
 548 
 549 	priv->timer_stopped = TRUE;
 550 	priv->extraction_timer_stopped = TRUE;
 551 
 552 	priv->items_created = tracker_priority_queue_new ();
 553 	priv->items_updated = tracker_priority_queue_new ();
 554 	priv->items_deleted = tracker_priority_queue_new ();
 555 	priv->items_moved = tracker_priority_queue_new ();
 556 	priv->items_writeback = tracker_priority_queue_new ();
 557 
 558 #ifdef EVENT_QUEUE_ENABLE_TRACE
 559 	priv->queue_status_timeout_id = g_timeout_add_seconds (EVENT_QUEUE_STATUS_TIMEOUT_SECS,
 560 	                                                       miner_fs_queues_status_trace_timeout_cb,
 561 	                                                       object);
 562 #endif /* PROCESSING_POOL_ENABLE_TRACE */
 563 
 564 	priv->items_ignore_next_update = g_hash_table_new_full (g_str_hash, g_str_equal,
 565 	                                                        (GDestroyNotify) g_free,
 566 	                                                        (GDestroyNotify) NULL);
 567 
 568 	/* Create processing pools */
 569 	priv->task_pool = tracker_task_pool_new (DEFAULT_WAIT_POOL_LIMIT);
 570 	g_signal_connect (priv->task_pool, "notify::limit-reached",
 571 	                  G_CALLBACK (task_pool_limit_reached_notify_cb), object);
 572 
 573 	priv->writeback_pool = tracker_task_pool_new (DEFAULT_WAIT_POOL_LIMIT);
 574 	g_signal_connect (priv->writeback_pool, "notify::limit-reached",
 575 	                  G_CALLBACK (task_pool_limit_reached_notify_cb), object);
 576 
 577 	/* Create the indexing tree */
 578 	priv->indexing_tree = tracker_indexing_tree_new ();
 579 	g_signal_connect (priv->indexing_tree, "directory-removed",
 580 	                  G_CALLBACK (indexing_tree_directory_removed),
 581 	                  object);
 582 
 583 	/* Create the file notifier */
 584 	priv->file_notifier = tracker_file_notifier_new (priv->indexing_tree);
 585 
 586 	g_signal_connect (priv->file_notifier, "file-created",
 587 	                  G_CALLBACK (file_notifier_file_created),
 588 	                  object);
 589 	g_signal_connect (priv->file_notifier, "file-updated",
 590 	                  G_CALLBACK (file_notifier_file_updated),
 591 	                  object);
 592 	g_signal_connect (priv->file_notifier, "file-deleted",
 593 	                  G_CALLBACK (file_notifier_file_deleted),
 594 	                  object);
 595 	g_signal_connect (priv->file_notifier, "file-moved",
 596 	                  G_CALLBACK (file_notifier_file_moved),
 597 	                  object);
 598 	g_signal_connect (priv->file_notifier, "directory-started",
 599 	                  G_CALLBACK (file_notifier_directory_started),
 600 	                  object);
 601 	g_signal_connect (priv->file_notifier, "directory-finished",
 602 	                  G_CALLBACK (file_notifier_directory_finished),
 603 	                  object);
 604 	g_signal_connect (priv->file_notifier, "finished",
 605 	                  G_CALLBACK (file_notifier_finished),
 606 	                  object);
 607 
 608 	priv->quark_ignore_file = g_quark_from_static_string ("tracker-ignore-file");
 609 	priv->quark_directory_found_crawling = g_quark_from_static_string ("tracker-directory-found-crawling");
 610 	priv->quark_attribute_updated = g_quark_from_static_string ("tracker-attribute-updated");
 611 	priv->quark_reentry_counter = g_quark_from_static_string ("tracker-reentry-counter");
 612 
 613 	priv->mtime_checking = TRUE;
 614 	priv->initial_crawling = TRUE;
 615 }
 616 
 617 static gboolean
 618 miner_fs_initable_init (GInitable     *initable,
 619                         GCancellable  *cancellable,
 620                         GError       **error)
 621 {
 622 	TrackerMinerFSPrivate *priv;
 623 	guint limit;
 624 
 625 	if (!miner_fs_initable_parent_iface->init (initable, cancellable, error)) {
 626 		return FALSE;
 627 	}
 628 
 629 	priv = TRACKER_MINER_FS_GET_PRIVATE (initable);
 630 
 631 	g_object_get (initable, "processing-pool-ready-limit", &limit, NULL);
 632 	priv->sparql_buffer = tracker_sparql_buffer_new (tracker_miner_get_connection (TRACKER_MINER (initable)),
 633 	                                                 limit);
 634 	g_signal_connect (priv->sparql_buffer, "notify::limit-reached",
 635 	                  G_CALLBACK (task_pool_limit_reached_notify_cb),
 636 	                  initable);
 637 
 638 	return TRUE;
 639 }
 640 
 641 static void
 642 miner_fs_initable_iface_init (GInitableIface *iface)
 643 {
 644 	miner_fs_initable_parent_iface = g_type_interface_peek_parent (iface);
 645 	iface->init = miner_fs_initable_init;
 646 }
 647 
 648 static void
 649 fs_finalize (GObject *object)
 650 {
 651 	TrackerMinerFSPrivate *priv;
 652 
 653 	priv = TRACKER_MINER_FS_GET_PRIVATE (object);
 654 
 655 	g_timer_destroy (priv->timer);
 656 	g_timer_destroy (priv->extraction_timer);
 657 
 658 	if (priv->item_queues_handler_id) {
 659 		g_source_remove (priv->item_queues_handler_id);
 660 		priv->item_queues_handler_id = 0;
 661 	}
 662 
 663 	if (priv->item_queue_blocker) {
 664 		g_object_unref (priv->item_queue_blocker);
 665 	}
 666 
 667 	tracker_file_notifier_stop (priv->file_notifier);
 668 
 669 	/* Cancel every pending task */
 670 	tracker_task_pool_foreach (priv->task_pool,
 671 	                           task_pool_cancel_foreach,
 672 	                           NULL);
 673 	g_object_unref (priv->task_pool);
 674 
 675 	g_object_unref (priv->writeback_pool);
 676 
 677 	if (priv->sparql_buffer) {
 678 		g_object_unref (priv->sparql_buffer);
 679 	}
 680 
 681 	tracker_priority_queue_foreach (priv->items_moved,
 682 	                                (GFunc) item_moved_data_free,
 683 	                                NULL);
 684 	tracker_priority_queue_unref (priv->items_moved);
 685 
 686 	tracker_priority_queue_foreach (priv->items_deleted,
 687 	                                (GFunc) g_object_unref,
 688 	                                NULL);
 689 	tracker_priority_queue_unref (priv->items_deleted);
 690 
 691 	tracker_priority_queue_foreach (priv->items_updated,
 692 	                                (GFunc) g_object_unref,
 693 	                                NULL);
 694 	tracker_priority_queue_unref (priv->items_updated);
 695 
 696 	tracker_priority_queue_foreach (priv->items_created,
 697 	                                (GFunc) g_object_unref,
 698 	                                NULL);
 699 	tracker_priority_queue_unref (priv->items_created);
 700 
 701 	tracker_priority_queue_foreach (priv->items_writeback,
 702 	                                (GFunc) item_writeback_data_free,
 703 	                                NULL);
 704 	tracker_priority_queue_unref (priv->items_writeback);
 705 
 706 	g_hash_table_unref (priv->items_ignore_next_update);
 707 
 708 	g_object_unref (priv->indexing_tree);
 709 	g_object_unref (priv->file_notifier);
 710 
 711 #ifdef EVENT_QUEUE_ENABLE_TRACE
 712 	if (priv->queue_status_timeout_id)
 713 		g_source_remove (priv->queue_status_timeout_id);
 714 #endif /* PROCESSING_POOL_ENABLE_TRACE */
 715 
 716 	G_OBJECT_CLASS (tracker_miner_fs_parent_class)->finalize (object);
 717 }
 718 
 719 static void
 720 fs_set_property (GObject      *object,
 721                  guint         prop_id,
 722                  const GValue *value,
 723                  GParamSpec   *pspec)
 724 {
 725 	TrackerMinerFS *fs = TRACKER_MINER_FS (object);
 726 
 727 	switch (prop_id) {
 728 	case PROP_THROTTLE:
 729 		tracker_miner_fs_set_throttle (TRACKER_MINER_FS (object),
 730 		                               g_value_get_double (value));
 731 		break;
 732 	case PROP_WAIT_POOL_LIMIT:
 733 		tracker_task_pool_set_limit (fs->priv->task_pool,
 734 		                             g_value_get_uint (value));
 735 		break;
 736 	case PROP_READY_POOL_LIMIT:
 737 		fs->priv->sparql_buffer_limit = g_value_get_uint (value);
 738 
 739 		if (fs->priv->sparql_buffer) {
 740 			tracker_task_pool_set_limit (TRACKER_TASK_POOL (fs->priv->sparql_buffer),
 741 			                             fs->priv->sparql_buffer_limit);
 742 		}
 743 		break;
 744 	case PROP_MTIME_CHECKING:
 745 		fs->priv->mtime_checking = g_value_get_boolean (value);
 746 		break;
 747 	case PROP_INITIAL_CRAWLING:
 748 		fs->priv->initial_crawling = g_value_get_boolean (value);
 749 		break;
 750 	default:
 751 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 752 		break;
 753 	}
 754 }
 755 
 756 static void
 757 fs_get_property (GObject    *object,
 758                  guint       prop_id,
 759                  GValue     *value,
 760                  GParamSpec *pspec)
 761 {
 762 	TrackerMinerFS *fs;
 763 
 764 	fs = TRACKER_MINER_FS (object);
 765 
 766 	switch (prop_id) {
 767 	case PROP_THROTTLE:
 768 		g_value_set_double (value, fs->priv->throttle);
 769 		break;
 770 	case PROP_WAIT_POOL_LIMIT:
 771 		g_value_set_uint (value,
 772 		                  tracker_task_pool_get_limit (fs->priv->task_pool));
 773 		break;
 774 	case PROP_READY_POOL_LIMIT:
 775 		g_value_set_uint (value, fs->priv->sparql_buffer_limit);
 776 		break;
 777 	case PROP_MTIME_CHECKING:
 778 		g_value_set_boolean (value, fs->priv->mtime_checking);
 779 		break;
 780 	case PROP_INITIAL_CRAWLING:
 781 		g_value_set_boolean (value, fs->priv->initial_crawling);
 782 		break;
 783 	default:
 784 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 785 		break;
 786 	}
 787 }
 788 
 789 static void
 790 task_pool_limit_reached_notify_cb (GObject    *object,
 791 				   GParamSpec *pspec,
 792 				   gpointer    user_data)
 793 {
 794 	if (!tracker_task_pool_limit_reached (TRACKER_TASK_POOL (object))) {
 795 		item_queue_handlers_set_up (TRACKER_MINER_FS (user_data));
 796 	}
 797 }
 798 
 799 static void
 800 miner_started (TrackerMiner *miner)
 801 {
 802 	TrackerMinerFS *fs;
 803 
 804 	fs = TRACKER_MINER_FS (miner);
 805 
 806 	fs->priv->been_started = TRUE;
 807 
 808 	tracker_info ("Initializing");
 809 
 810 	g_object_set (miner,
 811 	              "progress", 0.0,
 812 	              "status", "Initializing",
 813 	              "remaining-time", 0,
 814 	              NULL);
 815 
 816 	tracker_file_notifier_start (fs->priv->file_notifier);
 817 }
 818 
 819 static void
 820 miner_stopped (TrackerMiner *miner)
 821 {
 822 	tracker_info ("Idle");
 823 
 824 	g_object_set (miner,
 825 	              "progress", 1.0,
 826 	              "status", "Idle",
 827 	              "remaining-time", -1,
 828 	              NULL);
 829 }
 830 
 831 static void
 832 miner_paused (TrackerMiner *miner)
 833 {
 834 	TrackerMinerFS *fs;
 835 
 836 	fs = TRACKER_MINER_FS (miner);
 837 
 838 	fs->priv->is_paused = TRUE;
 839 
 840 	tracker_file_notifier_stop (fs->priv->file_notifier);
 841 
 842 	if (fs->priv->item_queues_handler_id) {
 843 		g_source_remove (fs->priv->item_queues_handler_id);
 844 		fs->priv->item_queues_handler_id = 0;
 845 	}
 846 }
 847 
 848 static void
 849 miner_resumed (TrackerMiner *miner)
 850 {
 851 	TrackerMinerFS *fs;
 852 
 853 	fs = TRACKER_MINER_FS (miner);
 854 
 855 	fs->priv->is_paused = FALSE;
 856 
 857 	tracker_file_notifier_start (fs->priv->file_notifier);
 858 
 859 	/* Only set up queue handler if we have items waiting to be
 860 	 * processed.
 861 	 */
 862 	if (tracker_miner_fs_has_items_to_process (fs)) {
 863 		item_queue_handlers_set_up (fs);
 864 	}
 865 }
 866 
 867 
 868 static void
 869 miner_ignore_next_update (TrackerMiner *miner, const GStrv urls)
 870 {
 871 	TrackerMinerFS *fs;
 872 	guint n;
 873 
 874 	fs = TRACKER_MINER_FS (miner);
 875 
 876 	for (n = 0; urls[n] != NULL; n++) {
 877 		g_hash_table_insert (fs->priv->items_ignore_next_update,
 878 		                     g_strdup (urls[n]),
 879 		                     GINT_TO_POINTER (TRUE));
 880 	}
 881 
 882 	item_queue_handlers_set_up (fs);
 883 }
 884 
 885 static void
 886 process_print_stats (TrackerMinerFS *fs)
 887 {
 888 	/* Only do this the first time, otherwise the results are
 889 	 * likely to be inaccurate. Devices can be added or removed so
 890 	 * we can't assume stats are correct.
 891 	 */
 892 	if (!fs->priv->shown_totals) {
 893 		fs->priv->shown_totals = TRUE;
 894 
 895 		tracker_info ("--------------------------------------------------");
 896 		tracker_info ("Total directories : %d (%d ignored)",
 897 		              fs->priv->total_directories_found,
 898 		              fs->priv->total_directories_ignored);
 899 		tracker_info ("Total files       : %d (%d ignored)",
 900 		              fs->priv->total_files_found,
 901 		              fs->priv->total_files_ignored);
 902 #if 0
 903 		tracker_info ("Total monitors    : %d",
 904 		              tracker_monitor_get_count (fs->priv->monitor));
 905 #endif
 906 		tracker_info ("Total processed   : %d (%d notified, %d with error)",
 907 		              fs->priv->total_files_processed,
 908 		              fs->priv->total_files_notified,
 909 		              fs->priv->total_files_notified_error);
 910 		tracker_info ("--------------------------------------------------\n");
 911 	}
 912 }
 913 
 914 static void
 915 process_stop (TrackerMinerFS *fs)
 916 {
 917 	/* Now we have finished crawling, print stats and enable monitor events */
 918 	process_print_stats (fs);
 919 
 920 	g_timer_stop (fs->priv->timer);
 921 	g_timer_stop (fs->priv->extraction_timer);
 922 
 923 	fs->priv->timer_stopped = TRUE;
 924 	fs->priv->extraction_timer_stopped = TRUE;
 925 
 926 	tracker_info ("Idle");
 927 
 928 	g_object_set (fs,
 929 	              "progress", 1.0,
 930 	              "status", "Idle",
 931 	              "remaining-time", 0,
 932 	              NULL);
 933 
 934 	g_signal_emit (fs, signals[FINISHED], 0,
 935 	               g_timer_elapsed (fs->priv->timer, NULL),
 936 	               fs->priv->total_directories_found,
 937 	               fs->priv->total_directories_ignored,
 938 	               fs->priv->total_files_found,
 939 	               fs->priv->total_files_ignored);
 940 
 941 	g_timer_stop (fs->priv->timer);
 942 	g_timer_stop (fs->priv->extraction_timer);
 943 
 944 	fs->priv->total_directories_found = 0;
 945 	fs->priv->total_directories_ignored = 0;
 946 	fs->priv->total_files_found = 0;
 947 	fs->priv->total_files_ignored = 0;
 948 
 949 	fs->priv->been_crawled = TRUE;
 950 }
 951 
 952 static ItemMovedData *
 953 item_moved_data_new (GFile *file,
 954                      GFile *source_file)
 955 {
 956 	ItemMovedData *data;
 957 
 958 	data = g_slice_new (ItemMovedData);
 959 	data->file = g_object_ref (file);
 960 	data->source_file = g_object_ref (source_file);
 961 
 962 	return data;
 963 }
 964 
 965 static void
 966 item_moved_data_free (ItemMovedData *data)
 967 {
 968 	g_object_unref (data->file);
 969 	g_object_unref (data->source_file);
 970 	g_slice_free (ItemMovedData, data);
 971 }
 972 
 973 static ItemWritebackData *
 974 item_writeback_data_new (GFile     *file,
 975                          GStrv      rdf_types,
 976                          GPtrArray *results)
 977 {
 978 	ItemWritebackData *data;
 979 
 980 	data = g_slice_new (ItemWritebackData);
 981 
 982 	data->file = g_object_ref (file);
 983 	data->results = g_ptr_array_ref (results);
 984 	data->rdf_types = g_strdupv (rdf_types);
 985 	data->cancellable = g_cancellable_new ();
 986 	data->notified = FALSE;
 987 
 988 	return data;
 989 }
 990 
 991 static void
 992 item_writeback_data_free (ItemWritebackData *data)
 993 {
 994 	g_object_unref (data->file);
 995 	g_ptr_array_unref (data->results);
 996 	g_strfreev (data->rdf_types);
 997 	g_object_unref (data->cancellable);
 998 	g_slice_free (ItemWritebackData, data);
 999 }
1000 
1001 static gboolean
1002 item_queue_is_blocked_by_file (TrackerMinerFS *fs,
1003                                GFile *file)
1004 {
1005 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
1006 
1007 	if (fs->priv->item_queue_blocker != NULL &&
1008 	    (fs->priv->item_queue_blocker == file ||
1009 	     g_file_equal (fs->priv->item_queue_blocker, file))) {
1010 		return TRUE;
1011 	}
1012 
1013 	return FALSE;
1014 }
1015 
1016 static void
1017 sparql_buffer_task_finished_cb (GObject      *object,
1018                                 GAsyncResult *result,
1019                                 gpointer      user_data)
1020 {
1021 	TrackerMinerFS *fs;
1022 	TrackerMinerFSPrivate *priv;
1023 	TrackerTask *task;
1024 	GFile *task_file;
1025 	GError *error = NULL;
1026 
1027 	fs = user_data;
1028 	priv = fs->priv;
1029 
1030 	if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
1031 	                                           &error)) {
1032 		g_critical ("Could not execute sparql: %s", error->message);
1033 		priv->total_files_notified_error++;
1034 		g_error_free (error);
1035 	}
1036 
1037 	task = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
1038 	task_file = tracker_task_get_file (task);
1039 
1040 	if (item_queue_is_blocked_by_file (fs, task_file)) {
1041 		g_object_unref (priv->item_queue_blocker);
1042 		priv->item_queue_blocker = NULL;
1043 	}
1044 
1045 	if (priv->item_queue_blocker != NULL) {
1046 		if (tracker_task_pool_get_size (TRACKER_TASK_POOL (object)) > 0) {
1047 			tracker_sparql_buffer_flush (TRACKER_SPARQL_BUFFER (object),
1048 			                             "Item queue still blocked after flush");
1049 		}
1050 	} else {
1051 		item_queue_handlers_set_up (fs);
1052 	}
1053 }
1054 
1055 static UpdateProcessingTaskContext *
1056 update_processing_task_context_new (TrackerMiner         *miner,
1057                                     gint                  priority,
1058                                     const gchar          *urn,
1059                                     const gchar          *parent_urn,
1060                                     GCancellable         *cancellable,
1061                                     TrackerSparqlBuilder *builder)
1062 {
1063 	UpdateProcessingTaskContext *ctxt;
1064 
1065 	ctxt = g_slice_new0 (UpdateProcessingTaskContext);
1066 	ctxt->miner = miner;
1067 	ctxt->urn = g_strdup (urn);
1068 	ctxt->parent_urn = g_strdup (parent_urn);
1069 	ctxt->priority = priority;
1070 
1071 	if (cancellable) {
1072 		ctxt->cancellable = g_object_ref (cancellable);
1073 	}
1074 
1075 	if (builder) {
1076 		ctxt->builder = g_object_ref (builder);
1077 	}
1078 
1079 	return ctxt;
1080 }
1081 
1082 static void
1083 update_processing_task_context_free (UpdateProcessingTaskContext *ctxt)
1084 {
1085 	g_free (ctxt->urn);
1086 	g_free (ctxt->parent_urn);
1087 
1088 	if (ctxt->cancellable) {
1089 		g_object_unref (ctxt->cancellable);
1090 	}
1091 
1092 	if (ctxt->builder) {
1093 		g_object_unref (ctxt->builder);
1094 	}
1095 
1096 	g_slice_free (UpdateProcessingTaskContext, ctxt);
1097 }
1098 
1099 static gboolean
1100 do_process_file (TrackerMinerFS *fs,
1101                  TrackerTask    *task)
1102 {
1103 	TrackerMinerFSPrivate *priv;
1104 	gboolean processing;
1105 	gboolean attribute_update_only;
1106 	gchar *uri;
1107 	GFile *task_file;
1108 	UpdateProcessingTaskContext *ctxt;
1109 
1110 	ctxt = tracker_task_get_data (task);
1111 	task_file = tracker_task_get_file (task);
1112 	uri = g_file_get_uri (task_file);
1113 	priv = fs->priv;
1114 
1115 	attribute_update_only = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (task_file),
1116 	                                                             priv->quark_attribute_updated));
1117 
1118 	if (!attribute_update_only) {
1119 		g_debug ("Processing file '%s'...", uri);
1120 		g_signal_emit (fs, signals[PROCESS_FILE], 0,
1121 		               task_file,
1122 		               ctxt->builder,
1123 		               ctxt->cancellable,
1124 		               &processing);
1125 	} else {
1126 		g_debug ("Processing attributes in file '%s'...", uri);
1127 		g_signal_emit (fs, signals[PROCESS_FILE_ATTRIBUTES], 0,
1128 		               task_file,
1129 		               ctxt->builder,
1130 		               ctxt->cancellable,
1131 		               &processing);
1132 	}
1133 
1134 	if (!processing) {
1135 		/* Re-fetch data, since it might have been
1136 		 * removed in broken implementations
1137 		 */
1138 		task = tracker_task_pool_find (priv->task_pool, task_file);
1139 
1140 		g_message ("%s refused to process '%s'", G_OBJECT_TYPE_NAME (fs), uri);
1141 
1142 		if (!task) {
1143 			g_critical ("%s has returned FALSE in ::process-file for '%s', "
1144 			            "but it seems that this file has been processed through "
1145 			            "tracker_miner_fs_file_notify(), this is an "
1146 			            "implementation error", G_OBJECT_TYPE_NAME (fs), uri);
1147 		} else {
1148 			tracker_task_pool_remove (priv->task_pool, task);
1149 			tracker_task_unref (task);
1150 		}
1151 	}
1152 
1153 	g_free (uri);
1154 
1155 	return processing;
1156 }
1157 
1158 static void
1159 item_add_or_update_cb (TrackerMinerFS *fs,
1160                        TrackerTask    *extraction_task,
1161                        const GError   *error)
1162 {
1163 	UpdateProcessingTaskContext *ctxt;
1164 	TrackerTask *sparql_task = NULL;
1165 	GFile *task_file;
1166 	gchar *uri;
1167 
1168 	ctxt = tracker_task_get_data (extraction_task);
1169 	task_file = tracker_task_get_file (extraction_task);
1170 	uri = g_file_get_uri (task_file);
1171 
1172 	tracker_task_pool_remove (fs->priv->task_pool, extraction_task);
1173 
1174 	if (error) {
1175 		g_message ("Could not process '%s': %s", uri, error->message);
1176 
1177 		fs->priv->total_files_notified_error++;
1178 
1179 		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
1180 		    !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
1181 			sparql_task = tracker_sparql_task_new_with_sparql (task_file,
1182 			                                                   ctxt->builder);
1183 		}
1184 	} else {
1185 		if (ctxt->urn) {
1186 			gboolean attribute_update_only;
1187 
1188 			attribute_update_only = GPOINTER_TO_INT (g_object_steal_qdata (G_OBJECT (task_file),
1189 			                                                               fs->priv->quark_attribute_updated));
1190 			g_debug ("Updating item '%s' with urn '%s'%s",
1191 			         uri,
1192 			         ctxt->urn,
1193 			         attribute_update_only ? " (attributes only)" : "");
1194 
1195 			if (!attribute_update_only) {
1196 				gchar *full_sparql;
1197 
1198 				/* Update, delete all statements inserted by miner except:
1199 				 *  - rdf:type statements as they could cause implicit deletion of user data
1200 				 *  - nie:contentCreated so it persists across updates
1201 				 *
1202 				 * Additionally, delete also nie:url as it might have been set by 3rd parties,
1203 				 * and it's used to know whether a file is known to tracker or not.
1204 				 */
1205 				full_sparql = g_strdup_printf ("DELETE {"
1206 				                               "  GRAPH <%s> {"
1207 				                               "    <%s> ?p ?o"
1208 				                               "  } "
1209 				                               "} "
1210 				                               "WHERE {"
1211 				                               "  GRAPH <%s> {"
1212 				                               "    <%s> ?p ?o"
1213 				                               "    FILTER (?p != rdf:type && ?p != nie:contentCreated)"
1214 				                               "  } "
1215 				                               "} "
1216 				                               "DELETE {"
1217 				                               "  <%s> nie:url ?o"
1218 				                               "} WHERE {"
1219 				                               "  <%s> nie:url ?o"
1220 				                               "}"
1221 				                               "%s",
1222 				                               TRACKER_MINER_FS_GRAPH_URN, ctxt->urn,
1223 				                               TRACKER_MINER_FS_GRAPH_URN, ctxt->urn,
1224 				                               ctxt->urn, ctxt->urn,
1225 				                               tracker_sparql_builder_get_result (ctxt->builder));
1226 
1227 				sparql_task = tracker_sparql_task_new_take_sparql_str (task_file, full_sparql);
1228 			} else {
1229 				/* Do not drop graph if only updating attributes, the SPARQL builder
1230 				 * will already contain the necessary DELETE statements for the properties
1231 				 * being updated */
1232 				sparql_task = tracker_sparql_task_new_with_sparql (task_file, ctxt->builder);
1233 			}
1234 		} else {
1235 			g_debug ("Creating new item '%s'", uri);
1236 			sparql_task = tracker_sparql_task_new_with_sparql (task_file, ctxt->builder);
1237 		}
1238 	}
1239 
1240 	if (sparql_task) {
1241 		tracker_sparql_buffer_push (fs->priv->sparql_buffer,
1242 		                            sparql_task,
1243 		                            ctxt->priority,
1244 		                            sparql_buffer_task_finished_cb,
1245 		                            fs);
1246 
1247 		if (item_queue_is_blocked_by_file (fs, task_file)) {
1248 			tracker_sparql_buffer_flush (fs->priv->sparql_buffer, "Current file is blocking item queue");
1249 		}
1250 	} else {
1251 		if (item_queue_is_blocked_by_file (fs, task_file)) {
1252 			/* Make sure that we don't stall the item queue, although we could
1253 			 * expect the file to be reenqueued until the loop detector makes
1254 			 * us drop it since we were specifically waiting for it to complete.
1255 			 */
1256 			g_object_unref (fs->priv->item_queue_blocker);
1257 			fs->priv->item_queue_blocker = NULL;
1258 			item_queue_handlers_set_up (fs);
1259 		}
1260 	}
1261 
1262 	if (tracker_miner_fs_has_items_to_process (fs) == FALSE &&
1263 	    tracker_task_pool_get_size (TRACKER_TASK_POOL (fs->priv->task_pool)) == 0) {
1264 		/* We need to run this one more time to trigger process_stop() */
1265 		item_queue_handlers_set_up (fs);
1266 	}
1267 
1268 	tracker_task_unref (extraction_task);
1269 
1270 	g_free (uri);
1271 }
1272 
1273 static gboolean
1274 item_add_or_update (TrackerMinerFS *fs,
1275                     GFile          *file,
1276                     gint            priority,
1277                     gboolean        is_new)
1278 {
1279 	TrackerMinerFSPrivate *priv;
1280 	TrackerSparqlBuilder *sparql;
1281 	GCancellable *cancellable;
1282 	gboolean retval;
1283 	TrackerTask *task;
1284 	const gchar *parent_urn, *urn = NULL;
1285 	UpdateProcessingTaskContext *ctxt;
1286 	GFile *parent;
1287 
1288 	priv = fs->priv;
1289 	retval = TRUE;
1290 
1291 	cancellable = g_cancellable_new ();
1292 	sparql = tracker_sparql_builder_new_update ();
1293 	g_object_ref (file);
1294 
1295 	/* Always query. No matter we are notified the file was just
1296 	 * created, its meta data might already be in the store
1297 	 * (possibly inserted by other application) - in such a case
1298 	 * we have to UPDATE, not INSERT. */
1299 	urn = tracker_file_notifier_get_file_iri (fs->priv->file_notifier, file);
1300 
1301 	if (!tracker_indexing_tree_file_is_root (fs->priv->indexing_tree, file)) {
1302 		parent = g_file_get_parent (file);
1303 		parent_urn = tracker_file_notifier_get_file_iri (fs->priv->file_notifier, parent);
1304 		g_object_unref (parent);
1305 	} else {
1306 		parent_urn = NULL;
1307 	}
1308 
1309 	/* Create task and add it to the pool as a WAIT task (we need to extract
1310 	 * the file metadata and such) */
1311 	ctxt = update_processing_task_context_new (TRACKER_MINER (fs),
1312 	                                           priority,
1313 	                                           urn,
1314 	                                           parent_urn,
1315 	                                           cancellable,
1316 	                                           sparql);
1317 	task = tracker_task_new (file, ctxt,
1318 	                         (GDestroyNotify) update_processing_task_context_free);
1319 	tracker_task_pool_add (priv->task_pool, task);
1320 
1321 	if (do_process_file (fs, task)) {
1322 		fs->priv->total_files_processed++;
1323 
1324 		if (tracker_task_pool_limit_reached (priv->task_pool)) {
1325 			retval = FALSE;
1326 		}
1327 	}
1328 
1329 	g_object_unref (file);
1330 	g_object_unref (cancellable);
1331 	g_object_unref (sparql);
1332 
1333 	return retval;
1334 }
1335 
1336 static gboolean
1337 item_remove (TrackerMinerFS *fs,
1338              GFile          *file,
1339              gboolean        only_children)
1340 {
1341 	gchar *uri;
1342 	TrackerTask *task;
1343 	guint flags = 0;
1344 
1345 	uri = g_file_get_uri (file);
1346 
1347 	g_debug ("Removing item: '%s' (Deleted from filesystem or no longer monitored)",
1348 	         uri);
1349 
1350 	if (!only_children) {
1351 		flags = TRACKER_BULK_MATCH_EQUALS;
1352 	} else {
1353 		tracker_thumbnailer_remove_add (uri, NULL);
1354 		tracker_media_art_queue_remove (uri, NULL);
1355 	}
1356 
1357 	/* FIRST:
1358 	 * Remove tracker:available for the resources we're going to remove.
1359 	 * This is done so that unavailability of the resources is marked as soon
1360 	 * as possible, as the actual delete may take reaaaally a long time
1361 	 * (removing resources for 30GB of files takes even 30minutes in a 1-CPU
1362 	 * device). */
1363 
1364 	/* Add new task to processing pool */
1365 	task = tracker_sparql_task_new_bulk (file,
1366 	                                     "DELETE { "
1367 	                                     "  ?f tracker:available true "
1368 	                                     "}",
1369 	                                     flags | TRACKER_BULK_MATCH_CHILDREN);
1370 
1371 	tracker_sparql_buffer_push (fs->priv->sparql_buffer,
1372 	                            task,
1373 	                            G_PRIORITY_DEFAULT,
1374 	                            sparql_buffer_task_finished_cb,
1375 	                            fs);
1376 
1377 	/* SECOND:
1378 	 * Actually remove all resources. This operation is the one which may take
1379 	 * a long time.
1380 	 */
1381 
1382 	/* Add new task to processing pool */
1383 	task = tracker_sparql_task_new_bulk (file,
1384 	                                     "DELETE { "
1385 	                                     "  ?f a rdfs:Resource . "
1386 	                                     "  ?ie a rdfs:Resource "
1387 	                                     "}",
1388 	                                     flags |
1389 	                                     TRACKER_BULK_MATCH_CHILDREN |
1390 	                                     TRACKER_BULK_MATCH_LOGICAL_RESOURCES);
1391 
1392 	tracker_sparql_buffer_push (fs->priv->sparql_buffer,
1393 	                            task,
1394 	                            G_PRIORITY_DEFAULT,
1395 	                            sparql_buffer_task_finished_cb,
1396 	                            fs);
1397 
1398 	if (!tracker_task_pool_limit_reached (TRACKER_TASK_POOL (fs->priv->sparql_buffer))) {
1399 		item_queue_handlers_set_up (fs);
1400 	}
1401 
1402 	g_free (uri);
1403 
1404 	return TRUE;
1405 }
1406 
1407 static gboolean
1408 item_ignore_next_update (TrackerMinerFS *fs,
1409                          GFile          *file,
1410                          GFile          *source_file)
1411 {
1412 	TrackerSparqlBuilder *sparql;
1413 	gchar *uri;
1414 	gboolean success = FALSE;
1415 	GCancellable *cancellable;
1416 	GFile *working_file;
1417 
1418 	/* While we are in ignore-on-next-update:
1419 	 * o. We always ignore deletes because it's never the final operation
1420 	 *    of a write. We have a delete when both are null.
1421 	 * o. A create means the write used rename(). This is the final
1422 	 *    operation of a write and thus we make the update query.
1423 	 *    We have a create when file == null and source_file != null
1424 	 * o. A move means the write used rename(). This is the final
1425 	 *    operation of a write and thus we make the update query.
1426 	 *    We have a move when both file and source_file aren't null.
1427 	 * o. A update means the write didn't use rename(). This is the
1428 	 *    final operation of a write and thus we make the update query.
1429 	 *    An update means that file != null and source_file == null. */
1430 
1431 	/* Happens on delete while in write */
1432 	if (!file && !source_file) {
1433 		return TRUE;
1434 	}
1435 
1436 	/* Create or update, we are the final one so we make the update query */
1437 
1438 	if (!file && source_file) {
1439 		/* Happens on create while in write */
1440 		working_file = source_file;
1441 	} else {
1442 		/* Happens on update while in write */
1443 		working_file = file;
1444 	}
1445 
1446 	uri = g_file_get_uri (working_file);
1447 
1448 	g_debug ("Updating item: '%s' (IgnoreNextUpdate event)", uri);
1449 
1450 	cancellable = g_cancellable_new ();
1451 	sparql = tracker_sparql_builder_new_update ();
1452 	g_object_ref (working_file);
1453 
1454 	/* IgnoreNextUpdate */
1455 	g_signal_emit (fs, signals[IGNORE_NEXT_UPDATE_FILE], 0,
1456 	               working_file, sparql, cancellable, &success);
1457 
1458 	if (success) {
1459 		gchar *query;
1460 
1461 		/* Perhaps we should move the DELETE to tracker-miner-files.c?
1462 		 * Or we add support for DELETE to TrackerSparqlBuilder ofcrs */
1463 
1464 		query = g_strdup_printf ("DELETE { GRAPH <%s> { "
1465 		                         "  ?u nfo:fileSize ?unknown1 ; "
1466 		                         "     nfo:fileLastModified ?unknown2 ; "
1467 		                         "     nfo:fileLastAccessed ?unknown3 ; "
1468 		                         "     nie:mimeType ?unknown4 } "
1469 		                         "} WHERE { GRAPH <%s> { "
1470 		                         "  ?u nfo:fileSize ?unknown1 ; "
1471 		                         "     nfo:fileLastModified ?unknown2 ; "
1472 		                         "     nfo:fileLastAccessed ?unknown3 ; "
1473 		                         "     nie:mimeType ?unknown4 ; "
1474 		                         "     nie:url \"%s\" } "
1475 		                         "} %s", TRACKER_MINER_FS_GRAPH_URN,
1476 		                         TRACKER_MINER_FS_GRAPH_URN, uri,
1477 		                         tracker_sparql_builder_get_result (sparql));
1478 
1479 		tracker_sparql_connection_update_async (tracker_miner_get_connection (TRACKER_MINER (fs)),
1480 		                                        query,
1481 		                                        G_PRIORITY_DEFAULT,
1482 		                                        NULL,
1483 		                                        NULL,
1484 		                                        NULL);
1485 
1486 		g_free (query);
1487 	}
1488 
1489 	g_hash_table_remove (fs->priv->items_ignore_next_update, uri);
1490 
1491 	g_object_unref (sparql);
1492 	g_object_unref (working_file);
1493 	g_object_unref (cancellable);
1494 
1495 	g_free (uri);
1496 
1497 	return FALSE;
1498 }
1499 
1500 static void
1501 item_update_children_uri_cb (GObject      *object,
1502                              GAsyncResult *result,
1503                              gpointer      user_data)
1504 {
1505 	RecursiveMoveData *data = user_data;
1506 	GError *error = NULL;
1507 
1508 	TrackerSparqlCursor *cursor = tracker_sparql_connection_query_finish (TRACKER_SPARQL_CONNECTION (object), result, &error);
1509 
1510 	if (error) {
1511 		g_critical ("Could not query children: %s", error->message);
1512 		g_error_free (error);
1513 		if (cursor) {
1514 			g_object_unref (cursor);
1515 		}
1516 	} else {
1517 		while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
1518 			const gchar *child_source_uri, *child_mime, *child_urn;
1519 			gchar *child_uri;
1520 
1521 			child_urn = tracker_sparql_cursor_get_string (cursor, 0, NULL);
1522 			child_source_uri = tracker_sparql_cursor_get_string (cursor, 1, NULL);
1523 			child_mime = tracker_sparql_cursor_get_string (cursor, 2, NULL);
1524 
1525 			if (!g_str_has_prefix (child_source_uri, data->source_uri)) {
1526 				g_warning ("Child URI '%s' does not start with parent URI '%s'",
1527 				           child_source_uri,
1528 				           data->source_uri);
1529 				continue;
1530 			}
1531 
1532 			child_uri = g_strdup_printf ("%s%s", data->uri, child_source_uri + strlen (data->source_uri));
1533 
1534 			g_string_append_printf (data->sparql,
1535 			                        "DELETE { "
1536 			                        "  <%s> nie:url ?u "
1537 			                        "} WHERE { "
1538 			                        "  <%s> nie:url ?u "
1539 			                        "} ",
1540 			                        child_urn, child_urn);
1541 
1542 			g_string_append_printf (data->sparql,
1543 			                        "INSERT INTO <%s> {"
1544 			                        "  <%s> nie:url \"%s\" "
1545 			                        "} ",
1546 			                        child_urn, child_urn, child_uri);
1547 
1548 			tracker_thumbnailer_move_add (child_source_uri, child_mime, child_uri);
1549 
1550 			g_free (child_uri);
1551 		}
1552 	}
1553 
1554 	g_object_unref (cursor);
1555 
1556 	g_main_loop_quit (data->main_loop);
1557 }
1558 
1559 static void
1560 item_update_children_uri (TrackerMinerFS    *fs,
1561                           RecursiveMoveData *move_data,
1562                           const gchar       *source_uri,
1563                           const gchar       *uri)
1564 {
1565 	gchar *sparql;
1566 
1567 	sparql = g_strdup_printf ("SELECT ?child ?url nie:mimeType(?child) WHERE { "
1568 	                          "  ?child nie:url ?url . "
1569 	                          "  FILTER (tracker:uri-is-descendant (\"%s\", ?url)) "
1570 	                          "}",
1571 	                          source_uri);
1572 
1573 	tracker_sparql_connection_query_async (tracker_miner_get_connection (TRACKER_MINER (fs)),
1574 	                                       sparql,
1575 	                                       NULL,
1576 	                                       item_update_children_uri_cb,
1577 	                                       move_data);
1578 
1579 	g_free (sparql);
1580 }
1581 
1582 static gboolean
1583 item_move (TrackerMinerFS *fs,
1584            GFile          *file,
1585            GFile          *source_file)
1586 {
1587 	gchar     *uri, *source_uri;
1588 	GFileInfo *file_info;
1589 	GString   *sparql;
1590 	RecursiveMoveData move_data;
1591 	TrackerTask *task;
1592 	const gchar *source_iri;
1593 	gchar *display_name;
1594 	gboolean source_exists;
1595 	GFile *new_parent;
1596 	const gchar *new_parent_iri;
1597 	TrackerDirectoryFlags source_flags, flags;
1598 
1599 	uri = g_file_get_uri (file);
1600 	source_uri = g_file_get_uri (source_file);
1601 
1602 	file_info = g_file_query_info (file,
1603 	                               G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
1604 	                               G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
1605 	                               G_FILE_ATTRIBUTE_STANDARD_TYPE,
1606 	                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
1607 	                               NULL, NULL);
1608 
1609 	/* Get 'source' ID */
1610 	source_iri = tracker_file_notifier_get_file_iri (fs->priv->file_notifier,
1611 	                                                 source_file);
1612 	source_exists = (source_iri != NULL);
1613 
1614 	if (!file_info) {
1615 		gboolean retval;
1616 
1617 		if (source_exists) {
1618 			/* Destination file has gone away, ignore dest file and remove source if any */
1619 			retval = item_remove (fs, source_file, FALSE);
1620 		} else {
1621 			/* Destination file went away, and source wasn't indexed either */
1622 			retval = TRUE;
1623 		}
1624 
1625 		g_free (source_uri);
1626 		g_free (uri);
1627 
1628 		return retval;
1629 	}
1630 
1631 	g_debug ("Moving item from '%s' to '%s'",
1632 	         source_uri,
1633 	         uri);
1634 
1635 	tracker_thumbnailer_move_add (source_uri,
1636 	                              g_file_info_get_content_type (file_info),
1637 	                              uri);
1638 
1639 	sparql = g_string_new ("");
1640 
1641 	/* Delete destination item from store if any */
1642 	g_string_append_printf (sparql,
1643 	                        "DELETE { "
1644 	                        "  ?urn a rdfs:Resource "
1645 	                        "} WHERE {"
1646 	                        "  ?urn nie:url \"%s\" "
1647 	                        "}",
1648 	                        uri);
1649 
1650 	g_string_append_printf (sparql,
1651 	                        "DELETE { "
1652 	                        "  <%s> nfo:fileName ?f ; "
1653 	                        "       nie:url ?u ; "
1654 	                        "       nie:isStoredAs ?s ; "
1655 	                        "       nfo:belongsToContainer ?b"
1656 	                        "} WHERE { "
1657 	                        "  <%s> nfo:fileName ?f ; "
1658 	                        "       nie:url ?u ; "
1659 	                        "       nie:isStoredAs ?s ; "
1660 	                        "       nfo:belongsToContainer ?b"
1661 	                        "} ",
1662 	                        source_iri, source_iri);
1663 
1664 	display_name = tracker_sparql_escape_string (g_file_info_get_display_name (file_info));
1665 
1666 	/* Get new parent information */
1667 	new_parent = g_file_get_parent (file);
1668 	new_parent_iri = tracker_file_notifier_get_file_iri (fs->priv->file_notifier,
1669 	                                                     new_parent);
1670 	if (new_parent && new_parent_iri) {
1671 		g_string_append_printf (sparql,
1672 		                        "INSERT INTO <%s> {"
1673 		                        "  <%s> nfo:fileName \"%s\" ; "
1674 		                        "       nie:url \"%s\" ; "
1675 		                        "       nie:isStoredAs <%s> ; "
1676 		                        "       nfo:belongsToContainer \"%s\""
1677 		                        "}"   ,
1678 		                        source_iri, source_iri,
1679 		                        display_name, uri,
1680 		                        source_iri,
1681 		                        new_parent_iri);
1682 	} else {
1683 		g_warning ("Adding moved item '%s' without nfo:belongsToContainer (new_parent: %p)",
1684 		           uri, new_parent);
1685 		g_string_append_printf (sparql,
1686 		                        "INSERT INTO <%s> {"
1687 		                        "  <%s> nfo:fileName \"%s\" ; "
1688 		                        "       nie:url \"%s\" ; "
1689 		                        "       nie:isStoredAs <%s>"
1690 		                        "} ",
1691 		                        source_iri, source_iri,
1692 		                        display_name, uri,
1693 		                        source_iri);
1694 	}
1695 
1696 	if (new_parent)
1697 		g_object_unref (new_parent);
1698 	g_free (display_name);
1699 
1700 	tracker_indexing_tree_get_root (fs->priv->indexing_tree, source_file, &source_flags);
1701 
1702 	if ((source_flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0 &&
1703 	    g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY) {
1704 		tracker_indexing_tree_get_root (fs->priv->indexing_tree,
1705 		                                file, &flags);
1706 
1707 		if ((flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0) {
1708 			/* Update children uris */
1709 			move_data.main_loop = g_main_loop_new (NULL, FALSE);
1710 			move_data.sparql = sparql;
1711 			move_data.source_uri = source_uri;
1712 			move_data.uri = uri;
1713 
1714 			item_update_children_uri (fs, &move_data, source_uri, uri);
1715 
1716 			g_main_loop_run (move_data.main_loop);
1717 
1718 			g_main_loop_unref (move_data.main_loop);
1719 		} else {
1720 			/* A directory is being moved from a recursive location to
1721 			 * a non-recursive one, mark all children as deleted.
1722 			 */
1723 			item_remove (fs, source_file, TRUE);
1724 		}
1725 	}
1726 
1727 	/* Add new task to processing pool */
1728 	task = tracker_sparql_task_new_take_sparql_str (file,
1729 	                                                g_string_free (sparql,
1730 	                                                               FALSE));
1731 	tracker_sparql_buffer_push (fs->priv->sparql_buffer,
1732 	                            task,
1733 	                            G_PRIORITY_DEFAULT,
1734 	                            sparql_buffer_task_finished_cb,
1735 	                            fs);
1736 
1737 	if (!tracker_task_pool_limit_reached (TRACKER_TASK_POOL (fs->priv->sparql_buffer))) {
1738 		item_queue_handlers_set_up (fs);
1739 	}
1740 
1741 	g_free (uri);
1742 	g_free (source_uri);
1743 	g_object_unref (file_info);
1744 
1745 	return TRUE;
1746 }
1747 
1748 static gboolean
1749 check_ignore_next_update (TrackerMinerFS *fs, GFile *queue_file)
1750 {
1751 	gchar *uri = g_file_get_uri (queue_file);
1752 	if (g_hash_table_lookup (fs->priv->items_ignore_next_update, uri)) {
1753 		g_free (uri);
1754 		return TRUE;
1755 	}
1756 	g_free (uri);
1757 	return FALSE;
1758 }
1759 
1760 static gboolean
1761 should_wait (TrackerMinerFS *fs,
1762              GFile          *file)
1763 {
1764 	GFile *parent;
1765 
1766 	/* Is the item already being processed? */
1767 	if (tracker_task_pool_find (fs->priv->task_pool, file) ||
1768 	    tracker_task_pool_find (fs->priv->writeback_pool, file) ||
1769 	    tracker_task_pool_find (TRACKER_TASK_POOL (fs->priv->sparql_buffer), file)) {
1770 		/* Yes, a previous event on same item currently
1771 		 * being processed */
1772 		fs->priv->item_queue_blocker = g_object_ref (file);
1773 		return TRUE;
1774 	}
1775 
1776 	/* Is the item's parent being processed right now? */
1777 	parent = g_file_get_parent (file);
1778 	if (parent) {
1779 		if (tracker_task_pool_find (fs->priv->task_pool, parent) ||
1780 		    tracker_task_pool_find (TRACKER_TASK_POOL (fs->priv->sparql_buffer), parent)) {
1781 			/* Yes, a previous event on the parent of this item
1782 			 * currently being processed */
1783 			fs->priv->item_queue_blocker = parent;
1784 			return TRUE;
1785 		}
1786 
1787 		g_object_unref (parent);
1788 	}
1789 	return FALSE;
1790 }
1791 
1792 static gboolean
1793 item_reenqueue_full (TrackerMinerFS       *fs,
1794                      TrackerPriorityQueue *item_queue,
1795                      GFile                *queue_file,
1796                      gpointer              queue_data,
1797                      gint                  priority)
1798 {
1799 	gint reentry_counter;
1800 	gchar *uri;
1801 	gboolean should_wait;
1802 
1803 	reentry_counter = GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (queue_file),
1804 	                                                       fs->priv->quark_reentry_counter));
1805 
1806 	if (reentry_counter < REENTRY_MAX) {
1807 		g_object_set_qdata (G_OBJECT (queue_file),
1808 		                    fs->priv->quark_reentry_counter,
1809 		                    GINT_TO_POINTER (reentry_counter + 1));
1810 		tracker_priority_queue_add (item_queue, queue_data, priority);
1811 
1812 		should_wait = TRUE;
1813 	} else {
1814 		uri = g_file_get_uri (queue_file);
1815 		g_warning ("File '%s' has been reenqueued more than %d times. It will not be indexed.", uri, REENTRY_MAX);
1816 		g_free (uri);
1817 
1818 		/* We must be careful not to return QUEUE_WAIT when there's actually
1819 		 * nothing left to wait for, or the crawling might never complete.
1820 		 */
1821 		if (tracker_miner_fs_has_items_to_process (fs)) {
1822 			should_wait = TRUE;
1823 		} else {
1824 			should_wait = FALSE;
1825 		}
1826 	}
1827 
1828 	return should_wait;
1829 }
1830 
1831 static gboolean
1832 item_reenqueue (TrackerMinerFS       *fs,
1833                 TrackerPriorityQueue *item_queue,
1834                 GFile                *queue_file,
1835                 gint                  priority)
1836 {
1837 	return item_reenqueue_full (fs, item_queue, queue_file, queue_file, priority);
1838 }
1839 
1840 static QueueState
1841 item_queue_get_next_file (TrackerMinerFS  *fs,
1842                           GFile          **file,
1843                           GFile          **source_file,
1844                           gint            *priority_out)
1845 {
1846 	ItemMovedData *data;
1847 	ItemWritebackData *wdata;
1848 	GFile *queue_file;
1849 	gint priority;
1850 
1851 	/* Writeback items first */
1852 	wdata = tracker_priority_queue_pop (fs->priv->items_writeback,
1853 	                                    &priority);
1854 	if (wdata) {
1855 		gboolean processing;
1856 
1857 		*file = g_object_ref (wdata->file);
1858 		*source_file = NULL;
1859 		*priority_out = priority;
1860 
1861 		trace_eq_pop_head ("WRITEBACK", wdata->file);
1862 
1863 		g_signal_emit (fs, signals[WRITEBACK_FILE], 0,
1864 		               wdata->file,
1865 		               wdata->rdf_types,
1866 		               wdata->results,
1867 		               wdata->cancellable,
1868 		               &processing);
1869 
1870 		if (processing) {
1871 			TrackerTask *task;
1872 
1873 			task = tracker_task_new (wdata->file, wdata,
1874 			                         (GDestroyNotify) item_writeback_data_free);
1875 			tracker_task_pool_add (fs->priv->writeback_pool, task);
1876 
1877 			return QUEUE_WRITEBACK;
1878 		} else {
1879 			item_writeback_data_free (wdata);
1880 		}
1881 	}
1882 
1883 	/* Deleted items second */
1884 	queue_file = tracker_priority_queue_pop (fs->priv->items_deleted,
1885 	                                         &priority);
1886 	if (queue_file) {
1887 		*source_file = NULL;
1888 
1889 		trace_eq_pop_head ("DELETED", queue_file);
1890 
1891 		/* Do not ignore DELETED event even if file is marked as
1892 		   IgnoreNextUpdate. We should never see DELETED on update
1893 		   (atomic rename or in-place update) but we may see DELETED
1894 		   due to actual file deletion right after update. */
1895 
1896 		/* If the same item OR its first parent is currently being processed,
1897 		 * we need to wait for this event */
1898 		if (should_wait (fs, queue_file)) {
1899 			*file = NULL;
1900 
1901 			trace_eq_push_head ("DELETED", queue_file, "Should wait");
1902 
1903 			/* Need to postpone event... */
1904 			if (item_reenqueue (fs, fs->priv->items_deleted, queue_file, priority - 1)) {
1905 				return QUEUE_WAIT;
1906 			} else {
1907 				return QUEUE_NONE;
1908 			}
1909 		}
1910 
1911 		*file = queue_file;
1912 		*priority_out = priority;
1913 		return QUEUE_DELETED;
1914 	}
1915 
1916 	/* Created items next */
1917 	queue_file = tracker_priority_queue_pop (fs->priv->items_created,
1918 	                                         &priority);
1919 	if (queue_file) {
1920 		*source_file = NULL;
1921 
1922 		trace_eq_pop_head ("CREATED", queue_file);
1923 
1924 		/* Note:
1925 		 * We won't be considering an IgnoreNextUpdate request if
1926 		 * the event being processed is a CREATED event and the
1927 		 * file was still unknown to tracker.
1928 		 */
1929 		if (check_ignore_next_update (fs, queue_file)) {
1930 			gchar *uri;
1931 
1932 			uri = g_file_get_uri (queue_file);
1933 
1934 			if (tracker_file_notifier_get_file_iri (fs->priv->file_notifier,
1935 			                                        queue_file) != NULL) {
1936 				g_debug ("CREATED event ignored on file '%s' as it already existed, "
1937 				         " processing as IgnoreNextUpdate...",
1938 				         uri);
1939 				g_free (uri);
1940 
1941 				return QUEUE_IGNORE_NEXT_UPDATE;
1942 			} else {
1943 				/* Just remove the IgnoreNextUpdate request */
1944 				g_debug ("Skipping the IgnoreNextUpdate request on CREATED event for '%s', file is actually new",
1945 				         uri);
1946 				g_hash_table_remove (fs->priv->items_ignore_next_update, uri);
1947 				g_free (uri);
1948 			}
1949 		}
1950 
1951 		/* If the same item OR its first parent is currently being processed,
1952 		 * we need to wait for this event */
1953 		if (should_wait (fs, queue_file)) {
1954 			*file = NULL;
1955 
1956 			trace_eq_push_head ("CREATED", queue_file, "Should wait");
1957 
1958 			/* Need to postpone event... */
1959 			if (item_reenqueue (fs, fs->priv->items_created, queue_file, priority - 1)) {
1960 				return QUEUE_WAIT;
1961 			} else {
1962 				return QUEUE_NONE;
1963 			}
1964 		}
1965 
1966 		*file = queue_file;
1967 		*priority_out = priority;
1968 		return QUEUE_CREATED;
1969 	}
1970 
1971 	/* Updated items next */
1972 	queue_file = tracker_priority_queue_pop (fs->priv->items_updated,
1973 	                                         &priority);
1974 	if (queue_file) {
1975 		*file = queue_file;
1976 		*source_file = NULL;
1977 
1978 		trace_eq_pop_head ("UPDATED", queue_file);
1979 
1980 		if (check_ignore_next_update (fs, queue_file)) {
1981 			gchar *uri;
1982 
1983 			uri = g_file_get_uri (queue_file);
1984 			g_debug ("UPDATED event ignored on file '%s', "
1985 			         " processing as IgnoreNextUpdate...",
1986 			         uri);
1987 			g_free (uri);
1988 
1989 			return QUEUE_IGNORE_NEXT_UPDATE;
1990 		}
1991 
1992 		/* If the same item OR its first parent is currently being processed,
1993 		 * we need to wait for this event */
1994 		if (should_wait (fs, queue_file)) {
1995 			*file = NULL;
1996 
1997 			trace_eq_push_head ("UPDATED", queue_file, "Should wait");
1998 
1999 			/* Need to postpone event... */
2000 			if (item_reenqueue (fs, fs->priv->items_updated, queue_file, priority - 1)) {
2001 				return QUEUE_WAIT;
2002 			} else {
2003 				return QUEUE_NONE;
2004 			}
2005 		}
2006 
2007 		*priority_out = priority;
2008 
2009 		return QUEUE_UPDATED;
2010 	}
2011 
2012 	/* Moved items next */
2013 	data = tracker_priority_queue_pop (fs->priv->items_moved,
2014 	                                   &priority);
2015 	if (data) {
2016 		trace_eq_pop_head_2 ("MOVED", data->file, data->source_file);
2017 
2018 		if (check_ignore_next_update (fs, data->file)) {
2019 			gchar *uri;
2020 			gchar *source_uri;
2021 
2022 			uri = g_file_get_uri (queue_file);
2023 			source_uri = g_file_get_uri (data->source_file);
2024 			g_debug ("MOVED event ignored on files '%s->%s', "
2025 			         " processing as IgnoreNextUpdate on '%s'",
2026 			         source_uri, uri, uri);
2027 			g_free (uri);
2028 			g_free (source_uri);
2029 
2030 			*file = g_object_ref (data->file);
2031 			*source_file = g_object_ref (data->source_file);
2032 			item_moved_data_free (data);
2033 			return QUEUE_IGNORE_NEXT_UPDATE;
2034 		}
2035 
2036 		/* If the same item OR its first parent is currently being processed,
2037 		 * we need to wait for this event */
2038 		if (should_wait (fs, data->file) ||
2039 		    should_wait (fs, data->source_file)) {
2040 			*file = NULL;
2041 			*source_file = NULL;
2042 
2043 			trace_eq_push_head_2 ("MOVED", data->source_file, data->file, "Should wait");
2044 
2045 			/* Need to postpone event... */
2046 			if (item_reenqueue_full (fs, fs->priv->items_moved, data->file, data, priority - 1)) {
2047 				return QUEUE_WAIT;
2048 			} else {
2049 				return QUEUE_NONE;
2050 			}
2051 		}
2052 
2053 		*file = g_object_ref (data->file);
2054 		*source_file = g_object_ref (data->source_file);
2055 		*priority_out = priority;
2056 		item_moved_data_free (data);
2057 		return QUEUE_MOVED;
2058 	}
2059 
2060 	*file = NULL;
2061 	*source_file = NULL;
2062 
2063 	if (tracker_file_notifier_is_active (fs->priv->file_notifier) ||
2064 	    tracker_task_pool_limit_reached (fs->priv->task_pool) ||
2065 	    tracker_task_pool_limit_reached (TRACKER_TASK_POOL (fs->priv->sparql_buffer))) {
2066 		if (tracker_task_pool_get_size (fs->priv->task_pool) == 0) {
2067 			fs->priv->extraction_timer_stopped = TRUE;
2068 			g_timer_stop (fs->priv->extraction_timer);
2069 		}
2070 
2071 		/* There are still pending items to crawl,
2072 		 * or extract pool limit is reached
2073 		 */
2074 		return QUEUE_WAIT;
2075 	}
2076 
2077 	return QUEUE_NONE;
2078 }
2079 
2080 static gdouble
2081 item_queue_get_progress (TrackerMinerFS *fs,
2082                          guint          *n_items_processed,
2083                          guint          *n_items_remaining)
2084 {
2085 	guint items_to_process = 0;
2086 	guint items_total = 0;
2087 
2088 	items_to_process += tracker_priority_queue_get_length (fs->priv->items_deleted);
2089 	items_to_process += tracker_priority_queue_get_length (fs->priv->items_created);
2090 	items_to_process += tracker_priority_queue_get_length (fs->priv->items_updated);
2091 	items_to_process += tracker_priority_queue_get_length (fs->priv->items_moved);
2092 	items_to_process += tracker_priority_queue_get_length (fs->priv->items_writeback);
2093 
2094 	items_total += fs->priv->total_directories_found;
2095 	items_total += fs->priv->total_files_found;
2096 
2097 	if (n_items_processed) {
2098 		*n_items_processed = ((items_total >= items_to_process) ?
2099 		                      (items_total - items_to_process) : 0);
2100 	}
2101 
2102 	if (n_items_remaining) {
2103 		*n_items_remaining = items_to_process;
2104 	}
2105 
2106 	if (items_total == 0 ||
2107 	    items_to_process == 0 ||
2108 	    items_to_process > items_total) {
2109 		return 1.0;
2110 	}
2111 
2112 	return (gdouble) (items_total - items_to_process) / items_total;
2113 }
2114 
2115 static gboolean
2116 item_queue_handlers_cb (gpointer user_data)
2117 {
2118 	TrackerMinerFS *fs = user_data;
2119 	GFile *file = NULL;
2120 	GFile *source_file = NULL;
2121 	GFile *parent;
2122 	QueueState queue;
2123 	GTimeVal time_now;
2124 	static GTimeVal time_last = { 0 };
2125 	gboolean keep_processing = TRUE;
2126 	gint priority = 0;
2127 
2128 	if (fs->priv->timer_stopped) {
2129 		g_timer_start (fs->priv->timer);
2130 		fs->priv->timer_stopped = FALSE;
2131 	}
2132 
2133 	if (tracker_task_pool_limit_reached (TRACKER_TASK_POOL (fs->priv->sparql_buffer))) {
2134 		/* Task pool is full, give it a break */
2135 		fs->priv->item_queues_handler_id = 0;
2136 		return FALSE;
2137 	}
2138 
2139 	queue = item_queue_get_next_file (fs, &file, &source_file, &priority);
2140 
2141 	if (queue == QUEUE_WAIT) {
2142 		/* Items are still being processed, so wait until
2143 		 * the processing pool is cleared before starting with
2144 		 * the next directories batch.
2145 		 */
2146 		fs->priv->item_queues_handler_id = 0;
2147 
2148 		/* We should flush the processing pool buffer here, because
2149 		 * if there was a previous task on the same file we want to
2150 		 * process now, we want it to get finished before we can go
2151 		 * on with the queues... */
2152 		tracker_sparql_buffer_flush (fs->priv->sparql_buffer,
2153 		                             "Queue handlers WAIT");
2154 
2155 		return FALSE;
2156 	}
2157 
2158 	if (file && queue != QUEUE_DELETED &&
2159 	    tracker_file_is_locked (file)) {
2160 		gchar *uri;
2161 
2162 		/* File is locked, ignore any updates on it */
2163 
2164 		uri = g_file_get_uri (file);
2165 		g_debug ("File '%s' is currently locked, ignoring updates on it",
2166 		         uri);
2167 		g_free (uri);
2168 
2169 		g_object_unref (file);
2170 
2171 		if (source_file) {
2172 			g_object_unref (source_file);
2173 		}
2174 
2175 		return TRUE;
2176 	}
2177 
2178 	if (queue == QUEUE_NONE) {
2179 		g_timer_stop (fs->priv->extraction_timer);
2180 		fs->priv->extraction_timer_stopped = TRUE;
2181 	} else if (fs->priv->extraction_timer_stopped) {
2182 		g_timer_continue (fs->priv->extraction_timer);
2183 		fs->priv->extraction_timer_stopped = FALSE;
2184 	}
2185 
2186 	/* Update progress, but don't spam it. */
2187 	g_get_current_time (&time_now);
2188 
2189 	if ((time_now.tv_sec - time_last.tv_sec) >= 1) {
2190 		guint items_processed, items_remaining;
2191 		gdouble progress_now;
2192 		static gdouble progress_last = 0.0;
2193 		static gint info_last = 0;
2194 		gdouble seconds_elapsed, extraction_elapsed;
2195 
2196 		time_last = time_now;
2197 
2198 		/* Update progress? */
2199 		progress_now = item_queue_get_progress (fs,
2200 		                                        &items_processed,
2201 		                                        &items_remaining);
2202 		seconds_elapsed = g_timer_elapsed (fs->priv->timer, NULL);
2203 		extraction_elapsed = g_timer_elapsed (fs->priv->extraction_timer, NULL);
2204 
2205 		if (!tracker_file_notifier_is_active (fs->priv->file_notifier)) {
2206 			gchar *status;
2207 			gint remaining_time;
2208 
2209 			g_object_get (fs, "status", &status, NULL);
2210 
2211 			/* Compute remaining time */
2212 			remaining_time = (gint)tracker_seconds_estimate (extraction_elapsed,
2213 			                                                 items_processed,
2214 			                                                 items_remaining);
2215 
2216 			/* CLAMP progress so it doesn't go back below
2217 			 * 2% (which we use for crawling)
2218 			 */
2219 			if (g_strcmp0 (status, "Processing…") != 0) {
2220 				/* Don't spam this */
2221 				tracker_info ("Processing…");
2222 				g_object_set (fs,
2223 				              "status", "Processing…",
2224 				              "progress", CLAMP (progress_now, 0.02, 1.00),
2225 				              "remaining-time", remaining_time,
2226 				              NULL);
2227 			} else {
2228 				g_object_set (fs,
2229 				              "progress", CLAMP (progress_now, 0.02, 1.00),
2230 				              "remaining-time", remaining_time,
2231 				              NULL);
2232 			}
2233 
2234 			g_free (status);
2235 		}
2236 
2237 		if (++info_last >= 5 &&
2238 		    (gint) (progress_last * 100) != (gint) (progress_now * 100)) {
2239 			gchar *str1, *str2;
2240 
2241 			info_last = 0;
2242 			progress_last = progress_now;
2243 
2244 			/* Log estimated remaining time */
2245 			str1 = tracker_seconds_estimate_to_string (extraction_elapsed,
2246 			                                           TRUE,
2247 			                                           items_processed,
2248 			                                           items_remaining);
2249 			str2 = tracker_seconds_to_string (seconds_elapsed, TRUE);
2250 
2251 			tracker_info ("Processed %u/%u, estimated %s left, %s elapsed",
2252 			              items_processed,
2253 			              items_processed + items_remaining,
2254 			              str1,
2255 			              str2);
2256 
2257 			g_free (str2);
2258 			g_free (str1);
2259 		}
2260 	}
2261 
2262 	/* Handle queues */
2263 	switch (queue) {
2264 	case QUEUE_NONE:
2265 		if (!tracker_file_notifier_is_active (fs->priv->file_notifier) &&
2266 		    tracker_task_pool_get_size (fs->priv->task_pool) == 0) {
2267 			if (tracker_task_pool_get_size (TRACKER_TASK_POOL (fs->priv->sparql_buffer)) == 0) {
2268 				/* Print stats and signal finished */
2269 				process_stop (fs);
2270 
2271 				tracker_thumbnailer_send ();
2272 				tracker_media_art_queue_empty (tracker_miner_get_connection (TRACKER_MINER (fs)));
2273 			} else {
2274 				/* Flush any possible pending update here */
2275 				tracker_sparql_buffer_flush (fs->priv->sparql_buffer,
2276 				                             "Queue handlers NONE");
2277 			}
2278 		}
2279 
2280 		/* No more files left to process */
2281 		keep_processing = FALSE;
2282 		break;
2283 	case QUEUE_MOVED:
2284 		keep_processing = item_move (fs, file, source_file);
2285 		break;
2286 	case QUEUE_DELETED:
2287 		keep_processing = item_remove (fs, file, FALSE);
2288 		break;
2289 	case QUEUE_CREATED:
2290 	case QUEUE_UPDATED:
2291 		parent = g_file_get_parent (file);
2292 
2293 		if (!parent ||
2294 		    tracker_indexing_tree_file_is_root (fs->priv->indexing_tree, file) ||
2295 		    tracker_file_notifier_get_file_iri (fs->priv->file_notifier, parent)) {
2296 			keep_processing = item_add_or_update (fs, file, priority,
2297 			                                      (queue == QUEUE_CREATED));
2298 		} else {
2299 			TrackerPriorityQueue *item_queue;
2300 			gchar *uri;
2301 
2302 			uri = g_file_get_uri (parent);
2303 			g_message ("Parent '%s' not indexed yet", uri);
2304 			g_free (uri);
2305 
2306 			if (queue == QUEUE_CREATED) {
2307 				item_queue = fs->priv->items_created;
2308 			} else {
2309 				item_queue = fs->priv->items_updated;
2310 			}
2311 
2312 			/* Parent isn't indexed yet, reinsert the task into the queue,
2313 			 * but forcily prepended by its parent so its indexing is
2314 			 * ensured, tasks are inserted at a higher priority so they
2315 			 * are processed promptly anyway.
2316 			 */
2317 			item_reenqueue (fs, item_queue, g_object_ref (parent), priority - 1);
2318 			item_reenqueue (fs, item_queue, g_object_ref (file), priority);
2319 
2320 			keep_processing = TRUE;
2321 		}
2322 
2323 		if (parent) {
2324 			g_object_unref (parent);
2325 		}
2326 
2327 		break;
2328 	case QUEUE_IGNORE_NEXT_UPDATE:
2329 		keep_processing = item_ignore_next_update (fs, file, source_file);
2330 		break;
2331 	case QUEUE_WRITEBACK:
2332 		/* Nothing to do here */
2333 		keep_processing = TRUE;
2334 		break;
2335 	default:
2336 		g_assert_not_reached ();
2337 	}
2338 
2339 	if (file) {
2340 		g_object_unref (file);
2341 	}
2342 
2343 	if (source_file) {
2344 		g_object_unref (source_file);
2345 	}
2346 
2347 	if (!keep_processing) {
2348 		fs->priv->item_queues_handler_id = 0;
2349 		return FALSE;
2350 	} else {
2351 		return TRUE;
2352 	}
2353 }
2354 
2355 static guint
2356 _tracker_idle_add (TrackerMinerFS *fs,
2357                    GSourceFunc     func,
2358                    gpointer        user_data)
2359 {
2360 	guint interval;
2361 
2362 	interval = TRACKER_MAX_TIMEOUT_INTERVAL * fs->priv->throttle;
2363 
2364 	if (interval == 0) {
2365 		return g_idle_add_full (TRACKER_TASK_PRIORITY, func, user_data, NULL);
2366 	} else {
2367 		return g_timeout_add_full (TRACKER_TASK_PRIORITY, interval, func, user_data, NULL);
2368 	}
2369 }
2370 
2371 static void
2372 item_queue_handlers_set_up (TrackerMinerFS *fs)
2373 {
2374 	trace_eq ("Setting up queue handlers...");
2375 	if (fs->priv->item_queues_handler_id != 0) {
2376 		trace_eq ("   cancelled: already one active");
2377 		return;
2378 	}
2379 
2380 	if (fs->priv->is_paused) {
2381 		trace_eq ("   cancelled: paused");
2382 		return;
2383 	}
2384 
2385 	if (fs->priv->item_queue_blocker) {
2386 		trace_eq ("   cancelled: item queue blocked waiting for file '%s'",
2387 		          g_file_get_path (fs->priv->item_queue_blocker));
2388 		return;
2389 	}
2390 
2391 	/* Already sent max number of tasks to tracker-extract/writeback? */
2392 	if (tracker_task_pool_limit_reached (fs->priv->task_pool) ||
2393 	    tracker_task_pool_limit_reached (fs->priv->writeback_pool)) {
2394 		trace_eq ("   cancelled: pool limit reached (tasks: %u (max %u) , writeback: %u (max %u))",
2395 		          tracker_task_pool_get_size (fs->priv->task_pool),
2396 		          tracker_task_pool_get_limit (fs->priv->task_pool),
2397 		          tracker_task_pool_get_size (fs->priv->writeback_pool),
2398 		          tracker_task_pool_get_limit (fs->priv->writeback_pool));
2399 		return;
2400 	}
2401 
2402 	if (tracker_task_pool_limit_reached (TRACKER_TASK_POOL (fs->priv->sparql_buffer))) {
2403 		trace_eq ("   cancelled: pool limit reached (sparql buffer: %u)",
2404 		          tracker_task_pool_get_limit (TRACKER_TASK_POOL (fs->priv->sparql_buffer)));
2405 		return;
2406 	}
2407 
2408 	if (!tracker_file_notifier_is_active (fs->priv->file_notifier)) {
2409 		gchar *status;
2410 		gdouble progress;
2411 
2412 		g_object_get (fs,
2413 		              "progress", &progress,
2414 		              "status", &status,
2415 		              NULL);
2416 
2417 		/* Don't spam this */
2418 		if (progress > 0.01 && g_strcmp0 (status, "Processing…") != 0) {
2419 			tracker_info ("Processing…");
2420 			g_object_set (fs, "status", "Processing…", NULL);
2421 		}
2422 
2423 		g_free (status);
2424 	}
2425 
2426 	trace_eq ("   scheduled in idle");
2427 	fs->priv->item_queues_handler_id =
2428 		_tracker_idle_add (fs,
2429 		                   item_queue_handlers_cb,
2430 		                   fs);
2431 }
2432 
2433 static gboolean
2434 should_check_file (TrackerMinerFS *fs,
2435                    GFile          *file,
2436                    gboolean        is_dir)
2437 {
2438 	GFileType file_type;
2439 
2440 	file_type = (is_dir) ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR;
2441 	return tracker_indexing_tree_file_is_indexable (fs->priv->indexing_tree,
2442 	                                                file, file_type);
2443 }
2444 
2445 static gboolean
2446 moved_files_equal (gconstpointer a,
2447                    gconstpointer b)
2448 {
2449 	const ItemMovedData *data = a;
2450 	GFile *file = G_FILE (b);
2451 
2452 	/* Compare with dest file */
2453 	return g_file_equal (data->file, file);
2454 }
2455 
2456 static gboolean
2457 writeback_files_equal (gconstpointer a,
2458                        gconstpointer b)
2459 {
2460 	const ItemWritebackData *data = a;
2461 	GFile *file = G_FILE (b);
2462 
2463 	/* Compare with dest file */
2464 	return g_file_equal (data->file, file);
2465 }
2466 
2467 static gboolean
2468 remove_writeback_task (TrackerMinerFS *fs,
2469                        GFile          *file)
2470 {
2471 	TrackerTask *task;
2472 	ItemWritebackData *data;
2473 
2474 	task = tracker_task_pool_find (fs->priv->writeback_pool, file);
2475 
2476 	if (!task) {
2477 		return FALSE;
2478 	}
2479 
2480 	data = tracker_task_get_data (task);
2481 
2482 	if (data->notified) {
2483 		tracker_task_pool_remove (fs->priv->writeback_pool, task);
2484 		tracker_task_unref (task);
2485 		return TRUE;
2486 	}
2487 
2488 	return FALSE;
2489 }
2490 
2491 static void
2492 cancel_writeback_task (TrackerMinerFS *fs,
2493                        GFile          *file)
2494 {
2495 	TrackerTask *task;
2496 
2497 	task = tracker_task_pool_find (fs->priv->writeback_pool, file);
2498 
2499 	if (task) {
2500 		ItemWritebackData *data;
2501 
2502 		data = tracker_task_get_data (task);
2503 		g_cancellable_cancel (data->cancellable);
2504 		tracker_task_pool_remove (fs->priv->writeback_pool, task);
2505 		tracker_task_unref (task);
2506 	}
2507 }
2508 
2509 /* Checks previous created/updated/deleted/moved/writeback queues for
2510  * monitor events. Returns TRUE if the item should still
2511  * be added to the queue.
2512  */
2513 static gboolean
2514 check_item_queues (TrackerMinerFS *fs,
2515                    QueueState      queue,
2516                    GFile          *file,
2517                    GFile          *other_file)
2518 {
2519 	ItemMovedData *move_data;
2520 
2521 	if (!fs->priv->been_crawled) {
2522 		/* Only do this after initial crawling, so
2523 		 * we are mostly sure that we won't be doing
2524 		 * checks on huge lists.
2525 		 */
2526 		return TRUE;
2527 	}
2528 
2529 	if (queue == QUEUE_UPDATED) {
2530 		TrackerTask *task;
2531 
2532 		if (other_file) {
2533 			task = tracker_task_pool_find (fs->priv->writeback_pool, other_file);
2534 		} else {
2535 			task = tracker_task_pool_find (fs->priv->writeback_pool, file);
2536 		}
2537 
2538 		if (task) {
2539 			/* There is a writeback task for
2540 			 * this file, so avoid any updates
2541 			 */
2542 			return FALSE;
2543 		}
2544 	}
2545 
2546 	switch (queue) {
2547 	case QUEUE_CREATED:
2548 		/* Created items aren't likely to have
2549 		 * anything in other queues for the same
2550 		 * file.
2551 		 */
2552 		return TRUE;
2553 	case QUEUE_UPDATED:
2554 		/* No further updates after a previous created/updated event */
2555 		if (tracker_priority_queue_find (fs->priv->items_created, NULL,
2556 		                                 (GEqualFunc) g_file_equal, file) ||
2557 		    tracker_priority_queue_find (fs->priv->items_updated, NULL,
2558 		                                 (GEqualFunc) g_file_equal, file)) {
2559 			g_debug ("  Found previous unhandled CREATED/UPDATED event");
2560 			return FALSE;
2561 		}
2562 	case QUEUE_WRITEBACK:
2563 		/* No consecutive writebacks for the same file */
2564 		if (tracker_priority_queue_find (fs->priv->items_writeback, NULL,
2565 		                                 writeback_files_equal, file)) {
2566 			g_debug ("  Found previous unhandled WRITEBACK event");
2567 			return FALSE;
2568 		}
2569 
2570 		return TRUE;
2571 	case QUEUE_DELETED:
2572 		if (tracker_task_pool_find (fs->priv->writeback_pool, file)) {
2573 			/* Cancel writeback operations on a deleted file */
2574 			cancel_writeback_task (fs, file);
2575 		}
2576 
2577 		/* Remove all previous updates */
2578 		if (tracker_priority_queue_foreach_remove (fs->priv->items_updated,
2579 		                                           (GEqualFunc) g_file_equal,
2580 		                                           file,
2581 		                                           (GDestroyNotify) g_object_unref)) {
2582 			g_debug ("  Deleting previous unhandled UPDATED event");
2583 		}
2584 
2585 		if (tracker_priority_queue_foreach_remove (fs->priv->items_created,
2586 		                                           (GEqualFunc) g_file_equal,
2587 		                                           file,
2588 		                                           (GDestroyNotify) g_object_unref)) {
2589 			/* Created event was still in the queue,
2590 			 * remove it and ignore the current event
2591 			 */
2592 			g_debug ("  Found matching unhandled CREATED event, removing file altogether");
2593 			return FALSE;
2594 		}
2595 
2596 		return TRUE;
2597 	case QUEUE_MOVED:
2598 		if (tracker_task_pool_find (fs->priv->writeback_pool, file)) {
2599 			/* If the origin file is also being written back,
2600 			 * cancel it as this is an external operation.
2601 			 */
2602 			cancel_writeback_task (fs, file);
2603 		}
2604 
2605 		/* Kill any events on other_file (The dest one), since it will be rewritten anyway */
2606 		if (tracker_priority_queue_foreach_remove (fs->priv->items_created,
2607 		                                           (GEqualFunc) g_file_equal,
2608 		                                           other_file,
2609 		                                           (GDestroyNotify) g_object_unref)) {
2610 			g_debug ("  Removing previous unhandled CREATED event for dest file, will be rewritten anyway");
2611 		}
2612 
2613 		if (tracker_priority_queue_foreach_remove (fs->priv->items_updated,
2614 		                                           (GEqualFunc) g_file_equal,
2615 		                                           other_file,
2616 		                                           (GDestroyNotify) g_object_unref)) {
2617 			g_debug ("  Removing previous unhandled UPDATED event for dest file, will be rewritten anyway");
2618 		}
2619 
2620 		/* Now check file (Origin one) */
2621 		if (tracker_priority_queue_foreach_remove (fs->priv->items_created,
2622 		                                           (GEqualFunc) g_file_equal,
2623 		                                           file,
2624 		                                           (GDestroyNotify) g_object_unref)) {
2625 			/* If source file was created, replace it with
2626 			 * a create event for the destination file, and
2627 			 * discard this event.
2628 			 *
2629 			 * We assume all posterior updates
2630 			 * have been merged together previously by this
2631 			 * same function.
2632 			 */
2633 			g_debug ("  Found matching unhandled CREATED event "
2634 			         "for source file, merging both events together");
2635 			tracker_priority_queue_add (fs->priv->items_created,
2636 			                            g_object_ref (other_file),
2637 			                            G_PRIORITY_DEFAULT);
2638 
2639 			return FALSE;
2640 		}
2641 
2642 		move_data = tracker_priority_queue_find (fs->priv->items_moved, NULL,
2643 		                                         (GEqualFunc) moved_files_equal, file);
2644 		if (move_data) {
2645 			/* Origin file was the dest of a previous
2646 			 * move operation, merge these together.
2647 			 */
2648 			g_debug ("  Source file is the destination of a previous "
2649 			         "unhandled MOVED event, merging both events together");
2650 			g_object_unref (move_data->file);
2651 			move_data->file = g_object_ref (other_file);
2652 			return FALSE;
2653 		}
2654 
2655 		return TRUE;
2656 		break;
2657 	default:
2658 		g_assert_not_reached ();
2659 	}
2660 
2661 	return TRUE;
2662 }
2663 
2664 static void
2665 file_notifier_file_created (TrackerFileNotifier  *notifier,
2666                             GFile                *file,
2667                             gpointer              user_data)
2668 {
2669 	TrackerMinerFS *fs = user_data;
2670 
2671 	if (check_item_queues (fs, QUEUE_CREATED, file, NULL)) {
2672 		tracker_priority_queue_add (fs->priv->items_created,
2673 		                            g_object_ref (file),
2674 		                            G_PRIORITY_DEFAULT);
2675 		item_queue_handlers_set_up (fs);
2676 	}
2677 }
2678 
2679 static void
2680 file_notifier_file_deleted (TrackerFileNotifier  *notifier,
2681                             GFile                *file,
2682                             gpointer              user_data)
2683 {
2684 	TrackerMinerFS *fs = user_data;
2685 
2686 	if (check_item_queues (fs, QUEUE_DELETED, file, NULL)) {
2687 		tracker_priority_queue_add (fs->priv->items_deleted,
2688 		                            g_object_ref (file),
2689 		                            G_PRIORITY_DEFAULT);
2690 		item_queue_handlers_set_up (fs);
2691 	}
2692 }
2693 
2694 static void
2695 file_notifier_file_updated (TrackerFileNotifier  *notifier,
2696                             GFile                *file,
2697                             gboolean              attributes_only,
2698                             gpointer              user_data)
2699 {
2700 	TrackerMinerFS *fs = user_data;
2701 
2702 	/* Writeback tasks would receive an updated after move,
2703 	 * consequence of the data being written back in the
2704 	 * copy, and its monitor events being propagated to
2705 	 * the destination file.
2706 	 */
2707 	if (!attributes_only &&
2708 	    remove_writeback_task (fs, file)) {
2709 		item_queue_handlers_set_up (fs);
2710 		return;
2711 	}
2712 
2713 	if (check_item_queues (fs, QUEUE_UPDATED, file, NULL)) {
2714 		if (attributes_only) {
2715 			g_object_set_qdata (G_OBJECT (file),
2716 			                    fs->priv->quark_attribute_updated,
2717 			                    GINT_TO_POINTER (TRUE));
2718 		}
2719 
2720 		tracker_priority_queue_add (fs->priv->items_updated,
2721 		                            g_object_ref (file),
2722 		                            G_PRIORITY_DEFAULT);
2723 		item_queue_handlers_set_up (fs);
2724 	}
2725 }
2726 
2727 static void
2728 file_notifier_file_moved (TrackerFileNotifier *notifier,
2729                           GFile               *source,
2730                           GFile               *dest,
2731                           gpointer             user_data)
2732 {
2733 	TrackerMinerFS *fs = user_data;
2734 
2735 	if (check_item_queues (fs, QUEUE_MOVED, source, dest)) {
2736 		tracker_priority_queue_add (fs->priv->items_moved,
2737 		                            item_moved_data_new (dest, source),
2738 		                            G_PRIORITY_DEFAULT);
2739 		item_queue_handlers_set_up (fs);
2740 	}
2741 }
2742 
2743 static void
2744 file_notifier_directory_started (TrackerFileNotifier *notifier,
2745                                  GFile               *directory,
2746                                  gpointer             user_data)
2747 {
2748 	TrackerMinerFS *fs = user_data;
2749 	TrackerDirectoryFlags flags;
2750 	gchar *str, *uri;
2751 
2752 	uri = g_file_get_uri (directory);
2753 	tracker_indexing_tree_get_root (fs->priv->indexing_tree,
2754 					directory, &flags);
2755 
2756 	if ((flags & TRACKER_DIRECTORY_FLAG_RECURSE) != 0) {
2757                 str = g_strdup_printf ("Crawling recursively directory '%s'", uri);
2758         } else {
2759                 str = g_strdup_printf ("Crawling single directory '%s'", uri);
2760         }
2761 
2762 	if (fs->priv->timer_stopped) {
2763 		g_timer_start (fs->priv->timer);
2764 		fs->priv->timer_stopped = FALSE;
2765 	}
2766 
2767 	if (fs->priv->extraction_timer_stopped) {
2768 		g_timer_start (fs->priv->timer);
2769 		fs->priv->extraction_timer_stopped = FALSE;
2770 	}
2771 
2772 	/* Always set the progress here to at least 1%, and the remaining time
2773          * to -1 as we cannot guess during crawling (we don't know how many directories
2774          * we will find) */
2775         g_object_set (fs,
2776                       "progress", 0.01,
2777                       "status", str,
2778                       "remaining-time", -1,
2779                       NULL);
2780 	g_free (str);
2781 	g_free (uri);
2782 }
2783 
2784 static void
2785 file_notifier_directory_finished (TrackerFileNotifier *notifier,
2786                                   GFile               *directory,
2787                                   guint                directories_found,
2788                                   guint                directories_ignored,
2789                                   guint                files_found,
2790                                   guint                files_ignored,
2791                                   gpointer             user_data)
2792 {
2793 	TrackerMinerFS *fs = user_data;
2794 
2795 	/* Update stats */
2796 	fs->priv->directories_found += directories_found;
2797 	fs->priv->directories_ignored += directories_ignored;
2798 	fs->priv->files_found += files_found;
2799 	fs->priv->files_ignored += files_ignored;
2800 
2801 	fs->priv->total_directories_found += directories_found;
2802 	fs->priv->total_directories_ignored += directories_ignored;
2803 	fs->priv->total_files_found += files_found;
2804 	fs->priv->total_files_ignored += files_ignored;
2805 }
2806 
2807 static void
2808 file_notifier_finished (TrackerFileNotifier *notifier,
2809                         gpointer             user_data)
2810 {
2811 	TrackerMinerFS *fs = user_data;
2812 
2813 	if (!tracker_miner_fs_has_items_to_process (fs)) {
2814 		tracker_info ("Finished all tasks");
2815 		process_stop (fs);
2816 	}
2817 }
2818 
2819 
2820 #ifdef CRAWLED_TREE_ENABLE_TRACE
2821 
2822 static gboolean
2823 print_file_tree (GNode    *node,
2824                  gpointer  user_data)
2825 {
2826 	gchar *name;
2827 	gint i;
2828 
2829 	name = g_file_get_basename (node->data);
2830 
2831 	/* Indentation */
2832 	for (i = g_node_depth (node) - 1; i > 0; i--) {
2833 		g_print ("  ");
2834 	}
2835 
2836 	g_print ("%s\n", name);
2837 	g_free (name);
2838 
2839 	return FALSE;
2840 }
2841 
2842 #endif /* CRAWLED_TREE_ENABLE_TRACE */
2843 
2844 /* Returns TRUE if file equals to
2845  * other_file, or is a child of it
2846  */
2847 static gboolean
2848 file_equal_or_descendant (GFile *file,
2849                           GFile *prefix)
2850 {
2851 	if (g_file_equal (file, prefix) ||
2852 	    g_file_has_prefix (file, prefix)) {
2853 		return TRUE;
2854 	}
2855 
2856 	return FALSE;
2857 }
2858 
2859 /**
2860  * tracker_miner_fs_directory_add:
2861  * @fs: a #TrackerMinerFS
2862  * @file: #GFile for the directory to inspect
2863  * @recurse: whether the directory should be inspected recursively
2864  *
2865  * Tells the filesystem miner to inspect a directory.
2866  *
2867  * Since: 0.8
2868  **/
2869 void
2870 tracker_miner_fs_directory_add (TrackerMinerFS *fs,
2871                                 GFile          *file,
2872                                 gboolean        recurse)
2873 {
2874 	TrackerDirectoryFlags flags;
2875 
2876 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
2877 	g_return_if_fail (G_IS_FILE (file));
2878 
2879 	flags = TRACKER_DIRECTORY_FLAG_MONITOR;
2880 
2881 	if (recurse) {
2882 		flags |= TRACKER_DIRECTORY_FLAG_RECURSE;
2883 	}
2884 
2885 	if (fs->priv->mtime_checking) {
2886 		flags |= TRACKER_DIRECTORY_FLAG_CHECK_MTIME;
2887 	}
2888 
2889 	tracker_indexing_tree_add (fs->priv->indexing_tree,
2890 	                           file, flags);
2891 }
2892 
2893 static void
2894 task_pool_cancel_foreach (gpointer data,
2895                           gpointer user_data)
2896 {
2897 	TrackerTask *task = data;
2898 	GFile *file = user_data;
2899 	GFile *task_file;
2900 	UpdateProcessingTaskContext *ctxt;
2901 
2902 	ctxt = tracker_task_get_data (task);
2903 	task_file = tracker_task_get_file (task);
2904 
2905 	if (ctxt &&
2906 	    ctxt->cancellable &&
2907 	    (!file ||
2908 	     (g_file_equal (task_file, file) ||
2909 	      g_file_has_prefix (task_file, file)))) {
2910 		g_cancellable_cancel (ctxt->cancellable);
2911 	}
2912 }
2913 
2914 static void
2915 writeback_pool_cancel_foreach (gpointer data,
2916                                gpointer user_data)
2917 {
2918 	GFile *task_file, *file;
2919 	TrackerTask *task;
2920 
2921 	task = data;
2922 	file = user_data;
2923 	task_file = tracker_task_get_file (task);
2924 
2925 	if (!file ||
2926 	    g_file_equal (task_file, file) ||
2927 	    g_file_has_prefix (task_file, file)) {
2928 		ItemWritebackData *task_data;
2929 
2930 		task_data = tracker_task_get_data (task);
2931 		g_cancellable_cancel (task_data->cancellable);
2932 	}
2933 }
2934 
2935 static void
2936 indexing_tree_directory_removed (TrackerIndexingTree *indexing_tree,
2937                                  GFile               *directory,
2938                                  gpointer             user_data)
2939 {
2940 	TrackerMinerFS *fs = user_data;
2941 	TrackerMinerFSPrivate *priv = fs->priv;
2942 	GTimer *timer = g_timer_new ();
2943 
2944 	/* Cancel all pending tasks on files inside the path given by file */
2945 	tracker_task_pool_foreach (priv->task_pool,
2946 	                           task_pool_cancel_foreach,
2947 	                           directory);
2948 
2949 	g_debug ("  Cancelled processing pool tasks at %f\n", g_timer_elapsed (timer, NULL));
2950 
2951 	tracker_task_pool_foreach (priv->writeback_pool,
2952 	                           writeback_pool_cancel_foreach,
2953 	                           directory);
2954 
2955 	g_debug ("  Cancelled writeback pool tasks at %f\n",
2956 	         g_timer_elapsed (timer, NULL));
2957 
2958 	/* Remove anything contained in the removed directory
2959 	 * from all relevant processing queues.
2960 	 */
2961 	tracker_priority_queue_foreach_remove (priv->items_updated,
2962 	                                       (GEqualFunc) file_equal_or_descendant,
2963 	                                       directory,
2964 	                                       (GDestroyNotify) g_object_unref);
2965 	tracker_priority_queue_foreach_remove (priv->items_created,
2966 	                                       (GEqualFunc) file_equal_or_descendant,
2967 	                                       directory,
2968 	                                       (GDestroyNotify) g_object_unref);
2969 
2970 	g_debug ("  Removed files at %f\n", g_timer_elapsed (timer, NULL));
2971 
2972 	g_message ("Finished remove directory operation in %f\n", g_timer_elapsed (timer, NULL));
2973 	g_timer_destroy (timer);
2974 }
2975 
2976 /**
2977  * tracker_miner_fs_directory_remove:
2978  * @fs: a #TrackerMinerFS
2979  * @file: #GFile for the directory to be removed
2980  *
2981  * Removes a directory from being inspected by @fs. Note that only directory
2982  *  watches are removed.
2983  *
2984  * Returns: %TRUE if the directory was successfully removed.
2985  *
2986  * Since: 0.8
2987  **/
2988 gboolean
2989 tracker_miner_fs_directory_remove (TrackerMinerFS *fs,
2990                                    GFile          *file)
2991 {
2992 	TrackerMinerFSPrivate *priv;
2993 
2994 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), FALSE);
2995 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
2996 
2997 	priv = fs->priv;
2998 
2999 	if (!tracker_indexing_tree_file_is_root (priv->indexing_tree, file)) {
3000 		return FALSE;
3001 	}
3002 
3003 	g_debug ("Removing directory");
3004 	tracker_indexing_tree_remove (priv->indexing_tree, file);
3005 
3006 	return TRUE;
3007 }
3008 
3009 
3010 /**
3011  * tracker_miner_fs_directory_remove_full:
3012  * @fs: a #TrackerMinerFS
3013  * @file: #GFile for the directory to be removed
3014  *
3015  * Removes a directory from being inspected by @fs, and removes all
3016  * associated metadata of the directory (and its contents) from the
3017  * store.
3018  *
3019  * Returns: %TRUE if the directory was successfully removed.
3020  *
3021  * Since: 0.10
3022  **/
3023 gboolean
3024 tracker_miner_fs_directory_remove_full (TrackerMinerFS *fs,
3025                                         GFile          *file)
3026 {
3027 	TrackerDirectoryFlags flags;
3028 
3029 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), FALSE);
3030 	g_return_val_if_fail (G_IS_FILE (file), FALSE);
3031 
3032 	tracker_indexing_tree_get_root (fs->priv->indexing_tree, file, &flags);
3033 
3034 	if (tracker_miner_fs_directory_remove (fs, file)) {
3035 		if ((flags & TRACKER_DIRECTORY_FLAG_PRESERVE) != 0) {
3036 			/* If the preserve flag is unset, TrackerFileNotifier
3037 			 * will delete automatically files from this config
3038 			 * directory, if it's set, we force the delete here
3039 			 * to preserve remove_full() semantics.
3040 			 */
3041 			trace_eq_push_tail ("DELETED", file, "on remove full");
3042 			tracker_priority_queue_add (fs->priv->items_deleted,
3043 			                            g_object_ref (file),
3044 			                            G_PRIORITY_DEFAULT);
3045 			item_queue_handlers_set_up (fs);
3046 		}
3047 
3048 		return TRUE;
3049 	}
3050 
3051 	return FALSE;
3052 }
3053 
3054 static gboolean
3055 check_file_parents (TrackerMinerFS *fs,
3056                     GFile          *file)
3057 {
3058 	GFile *parent, *root;
3059 	GList *parents = NULL, *p;
3060 
3061 	parent = g_file_get_parent (file);
3062 
3063 	if (!parent) {
3064 		return FALSE;
3065 	}
3066 
3067 	root = tracker_indexing_tree_get_root (fs->priv->indexing_tree,
3068 	                                       parent, NULL);
3069 	if (!root) {
3070 		g_object_unref (parent);
3071 		return FALSE;
3072 	}
3073 
3074 	/* Add parent directories until we're past the config dir */
3075 	while (parent &&
3076 	       !g_file_has_prefix (root, parent)) {
3077 		parents = g_list_prepend (parents, parent);
3078 		parent = g_file_get_parent (parent);
3079 	}
3080 
3081 	/* Last parent fetched is not added to the list */
3082 	if (parent) {
3083 		g_object_unref (parent);
3084 	}
3085 
3086 	for (p = parents; p; p = p->next) {
3087 		trace_eq_push_tail ("UPDATED", p->data, "checking file parents");
3088 		tracker_priority_queue_add (fs->priv->items_updated,
3089 		                            p->data,
3090 		                            G_PRIORITY_DEFAULT);
3091 	}
3092 
3093 	g_list_free (parents);
3094 
3095 	return TRUE;
3096 }
3097 
3098 /**
3099  * tracker_miner_fs_check_file_with_priority:
3100  * @fs: a #TrackerMinerFS
3101  * @file: #GFile for the file to check
3102  * @priority: the priority of the check task
3103  * @check_parents: whether to check parents and eligibility or not
3104  *
3105  * Tells the filesystem miner to check and index a file at
3106  * a given priority, this file must be part of the usual
3107  * crawling directories of #TrackerMinerFS. See
3108  * tracker_miner_fs_directory_add().
3109  *
3110  * Since: 0.10
3111  **/
3112 void
3113 tracker_miner_fs_check_file_with_priority (TrackerMinerFS *fs,
3114                                            GFile          *file,
3115                                            gint            priority,
3116                                            gboolean        check_parents)
3117 {
3118 	gboolean should_process = TRUE;
3119 	gchar *path;
3120 
3121 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3122 	g_return_if_fail (G_IS_FILE (file));
3123 
3124 	if (check_parents) {
3125 		should_process = should_check_file (fs, file, FALSE);
3126 	}
3127 
3128 	path = g_file_get_path (file);
3129 
3130 	g_debug ("%s:'%s' (FILE) (requested by application)",
3131 	         should_process ? "Found " : "Ignored",
3132 	         path);
3133 
3134 	if (should_process) {
3135 		if (check_parents && !check_file_parents (fs, file)) {
3136 			return;
3137 		}
3138 
3139 		trace_eq_push_tail ("UPDATED", file, "Requested by application");
3140 		tracker_priority_queue_add (fs->priv->items_updated,
3141 		                            g_object_ref (file),
3142 		                            priority);
3143 
3144 		item_queue_handlers_set_up (fs);
3145 	}
3146 
3147 	g_free (path);
3148 }
3149 
3150 
3151 /**
3152  * tracker_miner_fs_writeback_file:
3153  * @fs: a #TrackerMinerFS
3154  * @file: #GFile for the file to check
3155  * @rdf_types: A #GStrv with rdf types
3156  * @results: (element-type GStrv): A array of results from the preparation query
3157  *
3158  * Tells the filesystem miner to writeback a file.
3159  *
3160  * Since: 0.10.20
3161  **/
3162 void
3163 tracker_miner_fs_writeback_file (TrackerMinerFS *fs,
3164                                  GFile          *file,
3165                                  GStrv           rdf_types,
3166                                  GPtrArray      *results)
3167 {
3168 	gchar *path;
3169 	ItemWritebackData *data;
3170 
3171 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3172 	g_return_if_fail (G_IS_FILE (file));
3173 
3174 	path = g_file_get_path (file);
3175 
3176 	g_debug ("Performing write-back:'%s' (requested by application)", path);
3177 
3178 	trace_eq_push_tail ("WRITEBACK", file, "Requested by application");
3179 
3180 	data = item_writeback_data_new (file, rdf_types, results);
3181 	tracker_priority_queue_add (fs->priv->items_writeback, data,
3182 	                            G_PRIORITY_DEFAULT);
3183 
3184 	item_queue_handlers_set_up (fs);
3185 
3186 	g_free (path);
3187 }
3188 
3189 /**
3190  * tracker_miner_fs_writeback_notify:
3191  * @fs: a #TrackerMinerFS
3192  * @file: a #GFile
3193  * @error: a #GError with the error that happened during processing, or %NULL.
3194  *
3195  * Notifies @fs that all writing back on @file has been finished, if any error
3196  * happened during file data processing, it should be passed in @error, else
3197  * that parameter will contain %NULL to reflect success.
3198  *
3199  * Since: 0.10.20
3200  **/
3201 void
3202 tracker_miner_fs_writeback_notify (TrackerMinerFS *fs,
3203                                    GFile          *file,
3204                                    const GError   *error)
3205 {
3206 	TrackerTask *task;
3207 
3208 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3209 	g_return_if_fail (G_IS_FILE (file));
3210 
3211 	fs->priv->total_files_notified++;
3212 
3213 	task = tracker_task_pool_find (fs->priv->writeback_pool, file);
3214 
3215 	if (!task) {
3216 		gchar *uri;
3217 
3218 		uri = g_file_get_uri (file);
3219 		g_critical ("%s has notified that file '%s' has been written back, "
3220 		            "but that file was not in the task pool. "
3221 		            "This is an implementation error, please ensure that "
3222 		            "tracker_miner_fs_writeback_notify() is called on the same "
3223 		            "GFile that is passed in ::writeback-file, and that this"
3224 		            "signal didn't return FALSE for it",
3225 		            G_OBJECT_TYPE_NAME (fs), uri);
3226 		g_free (uri);
3227 	} else if (error) {
3228 
3229 		if (!(error->domain == TRACKER_DBUS_ERROR &&
3230 		      error->code == TRACKER_DBUS_ERROR_UNSUPPORTED)) {
3231 			g_warning ("Writeback operation failed: %s", error->message);
3232 		}
3233 
3234 		/* We don't expect any further monitor
3235 		 * events on the original file.
3236 		 */
3237 		tracker_task_pool_remove (fs->priv->writeback_pool, task);
3238 		tracker_task_unref (task);
3239 
3240 		item_queue_handlers_set_up (fs);
3241 	} else {
3242 		ItemWritebackData *data;
3243 
3244 		data = tracker_task_get_data (task);
3245 		data->notified = TRUE;
3246 	}
3247 
3248 	/* Check monitor_item_updated_cb() for the remainder of this notify,
3249 	 * as the last event happening on the written back file would be an
3250 	 * UPDATED event caused by the changes on the cloned file, followed
3251 	 * by a MOVE onto the original file, so the delayed update happens
3252 	 * on the destination file.
3253 	 */
3254 }
3255 
3256 /**
3257  * tracker_miner_fs_check_file:
3258  * @fs: a #TrackerMinerFS
3259  * @file: #GFile for the file to check
3260  * @check_parents: whether to check parents and eligibility or not
3261  *
3262  * Tells the filesystem miner to check and index a file,
3263  * this file must be part of the usual crawling directories
3264  * of #TrackerMinerFS. See tracker_miner_fs_directory_add().
3265  *
3266  * Since: 0.10
3267  **/
3268 void
3269 tracker_miner_fs_check_file (TrackerMinerFS *fs,
3270                              GFile          *file,
3271                              gboolean        check_parents)
3272 {
3273 	tracker_miner_fs_check_file_with_priority (fs, file,
3274 	                                           G_PRIORITY_HIGH,
3275 	                                           check_parents);
3276 }
3277 
3278 /**
3279  * tracker_miner_fs_check_directory_with_priority:
3280  * @fs: a #TrackerMinerFS
3281  * @file: #GFile for the directory to check
3282  * @priority: the priority of the check task
3283  * @check_parents: whether to check parents and eligibility or not
3284  *
3285  * Tells the filesystem miner to check and index a directory at
3286  * a given priority, this file must be part of the usual crawling
3287  * directories of #TrackerMinerFS. See tracker_miner_fs_directory_add().
3288  *
3289  * Since: 0.10
3290  **/
3291 void
3292 tracker_miner_fs_check_directory_with_priority (TrackerMinerFS *fs,
3293                                                 GFile          *file,
3294                                                 gint            priority,
3295                                                 gboolean        check_parents)
3296 {
3297 	gboolean should_process = TRUE;
3298 	gchar *path;
3299 
3300 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3301 	g_return_if_fail (G_IS_FILE (file));
3302 
3303 	if (check_parents) {
3304 		should_process = should_check_file (fs, file, TRUE);
3305 	}
3306 
3307 	path = g_file_get_path (file);
3308 
3309 	g_debug ("%s:'%s' (DIR) (requested by application)",
3310 	         should_process ? "Found " : "Ignored",
3311 	         path);
3312 
3313 	if (should_process) {
3314 		if (check_parents && !check_file_parents (fs, file)) {
3315 			return;
3316 		}
3317 
3318 		/* FIXME: Apply priority */
3319 		tracker_indexing_tree_add (fs->priv->indexing_tree,
3320 		                           file,
3321 		                           TRACKER_DIRECTORY_FLAG_RECURSE |
3322 		                           TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
3323 		                           TRACKER_DIRECTORY_FLAG_MONITOR);
3324 	}
3325 
3326 	g_free (path);
3327 }
3328 
3329 /**
3330  * tracker_miner_fs_check_directory:
3331  * @fs: a #TrackerMinerFS
3332  * @file: #GFile for the directory to check
3333  * @check_parents: whether to check parents and eligibility or not
3334  *
3335  * Tells the filesystem miner to check and index a directory,
3336  * this file must be part of the usual crawling directories
3337  * of #TrackerMinerFS. See tracker_miner_fs_directory_add().
3338  *
3339  * Since: 0.10
3340  **/
3341 void
3342 tracker_miner_fs_check_directory (TrackerMinerFS *fs,
3343                                   GFile          *file,
3344                                   gboolean        check_parents)
3345 {
3346 	tracker_miner_fs_check_directory_with_priority (fs, file,
3347 	                                                G_PRIORITY_DEFAULT,
3348 	                                                check_parents);
3349 }
3350 
3351 /**
3352  * tracker_miner_fs_file_notify:
3353  * @fs: a #TrackerMinerFS
3354  * @file: a #GFile
3355  * @error: a #GError with the error that happened during processing, or %NULL.
3356  *
3357  * Notifies @fs that all processing on @file has been finished, if any error
3358  * happened during file data processing, it should be passed in @error, else
3359  * that parameter will contain %NULL to reflect success.
3360  *
3361  * Since: 0.8
3362  **/
3363 void
3364 tracker_miner_fs_file_notify (TrackerMinerFS *fs,
3365                               GFile          *file,
3366                               const GError   *error)
3367 {
3368 	TrackerTask *task;
3369 
3370 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3371 	g_return_if_fail (G_IS_FILE (file));
3372 
3373 	fs->priv->total_files_notified++;
3374 
3375 	task = tracker_task_pool_find (fs->priv->task_pool, file);
3376 
3377 	if (!task) {
3378 		gchar *uri;
3379 
3380 		uri = g_file_get_uri (file);
3381 		g_critical ("%s has notified that file '%s' has been processed, "
3382 		            "but that file was not in the processing queue. "
3383 		            "This is an implementation error, please ensure that "
3384 		            "tracker_miner_fs_file_notify() is called on the same "
3385 		            "GFile that is passed in ::process-file, and that this"
3386 		            "signal didn't return FALSE for it",
3387 		            G_OBJECT_TYPE_NAME (fs), uri);
3388 		g_free (uri);
3389 
3390 		if (item_queue_is_blocked_by_file (fs, file)) {
3391 			/* Ensure we don't stall, although this is a very ugly situation */
3392 			g_object_unref (fs->priv->item_queue_blocker);
3393 			fs->priv->item_queue_blocker = NULL;
3394 			item_queue_handlers_set_up (fs);
3395 		}
3396 
3397 		return;
3398 	}
3399 
3400 	item_add_or_update_cb (fs, task, error);
3401 }
3402 
3403 /**
3404  * tracker_miner_fs_set_throttle:
3405  * @fs: a #TrackerMinerFS
3406  * @throttle: throttle value, between 0 and 1
3407  *
3408  * Tells the filesystem miner to throttle its operations.
3409  * a value of 0 means no throttling at all, so the miner
3410  * will perform operations at full speed, 1 is the slowest
3411  * value.
3412  *
3413  * Since: 0.8
3414  **/
3415 void
3416 tracker_miner_fs_set_throttle (TrackerMinerFS *fs,
3417                                gdouble         throttle)
3418 {
3419 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3420 
3421 	throttle = CLAMP (throttle, 0, 1);
3422 
3423 	if (fs->priv->throttle == throttle) {
3424 		return;
3425 	}
3426 
3427 	fs->priv->throttle = throttle;
3428 
3429 	/* Update timeouts */
3430 	if (fs->priv->item_queues_handler_id != 0) {
3431 		g_source_remove (fs->priv->item_queues_handler_id);
3432 
3433 		fs->priv->item_queues_handler_id =
3434 			_tracker_idle_add (fs,
3435 			                   item_queue_handlers_cb,
3436 			                   fs);
3437 	}
3438 }
3439 
3440 /**
3441  * tracker_miner_fs_get_throttle:
3442  * @fs: a #TrackerMinerFS
3443  *
3444  * Gets the current throttle value. see tracker_miner_fs_set_throttle().
3445  *
3446  * Returns: current throttle value.
3447  *
3448  * Since: 0.8
3449  **/
3450 gdouble
3451 tracker_miner_fs_get_throttle (TrackerMinerFS *fs)
3452 {
3453 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), 0);
3454 
3455 	return fs->priv->throttle;
3456 }
3457 
3458 /**
3459  * tracker_miner_fs_get_urn:
3460  * @fs: a #TrackerMinerFS
3461  * @file: a #GFile obtained in #TrackerMinerFS::process-file
3462  *
3463  * If the item exists in the store, this function retrieves
3464  * the URN for a #GFile being currently processed.
3465 
3466  * If @file is not being currently processed by @fs, or doesn't
3467  * exist in the store yet, %NULL will be returned.
3468  *
3469  * Returns: (transfer none): The URN containing the data associated to @file,
3470  *          or %NULL.
3471  *
3472  * Since: 0.8
3473  **/
3474 const gchar *
3475 tracker_miner_fs_get_urn (TrackerMinerFS *fs,
3476                           GFile          *file)
3477 {
3478 	TrackerTask *task;
3479 
3480 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), NULL);
3481 	g_return_val_if_fail (G_IS_FILE (file), NULL);
3482 
3483 	/* Check if found in currently processed data */
3484 	task = tracker_task_pool_find (fs->priv->task_pool, file);
3485 
3486 	if (!task) {
3487 		gchar *uri;
3488 
3489 		uri = g_file_get_uri (file);
3490 
3491 		g_critical ("File '%s' is not being currently processed, "
3492 		            "so the URN cannot be retrieved.", uri);
3493 		g_free (uri);
3494 
3495 		return NULL;
3496 	} else {
3497 		UpdateProcessingTaskContext *ctxt;
3498 
3499 		/* We are only storing the URN in the created/updated tasks */
3500 		ctxt = tracker_task_get_data (task);
3501 
3502 		if (!ctxt) {
3503 			gchar *uri;
3504 
3505 			uri = g_file_get_uri (file);
3506 			g_critical ("File '%s' is being processed, but not as a "
3507 			            "CREATED/UPDATED task, so cannot get URN",
3508 			            uri);
3509 			g_free (uri);
3510 			return NULL;
3511 		}
3512 
3513 		return ctxt->urn;
3514 	}
3515 }
3516 
3517 /**
3518  * tracker_miner_fs_query_urn:
3519  * @fs: a #TrackerMinerFS
3520  * @file: a #GFile
3521  *
3522  * If the item exists in the store, this function retrieves
3523  * the URN of the given #GFile
3524 
3525  * If @file doesn't exist in the store yet, %NULL will be returned.
3526  *
3527  * Returns: (transfer full): A newly allocated string with the URN containing the data associated
3528  *          to @file, or %NULL.
3529  *
3530  * Since: 0.10
3531  **/
3532 gchar *
3533 tracker_miner_fs_query_urn (TrackerMinerFS *fs,
3534                             GFile          *file)
3535 {
3536 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), NULL);
3537 	g_return_val_if_fail (G_IS_FILE (file), NULL);
3538 
3539 	return g_strdup (tracker_file_notifier_get_file_iri (fs->priv->file_notifier,
3540 	                                                     file));
3541 }
3542 
3543 /**
3544  * tracker_miner_fs_get_parent_urn:
3545  * @fs: a #TrackerMinerFS
3546  * @file: a #GFile obtained in #TrackerMinerFS::process-file
3547  *
3548  * If @file is currently being processed by @fs, this function
3549  * will return the parent folder URN if any. This function is
3550  * useful to set the nie:belongsToContainer relationship. The
3551  * processing order of #TrackerMinerFS guarantees that a folder
3552  * has been already fully processed for indexing before any
3553  * children is processed, so most usually this function should
3554  * return non-%NULL.
3555  *
3556  * Returns: (transfer none): The parent folder URN, or %NULL.
3557  *
3558  * Since: 0.8
3559  **/
3560 const gchar *
3561 tracker_miner_fs_get_parent_urn (TrackerMinerFS *fs,
3562                                  GFile          *file)
3563 {
3564 	TrackerTask *task;
3565 
3566 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), NULL);
3567 	g_return_val_if_fail (G_IS_FILE (file), NULL);
3568 
3569 	/* Check if found in currently processed data */
3570 	task = tracker_task_pool_find (fs->priv->task_pool, file);
3571 
3572 	if (!task) {
3573 		gchar *uri;
3574 
3575 		uri = g_file_get_uri (file);
3576 
3577 		g_critical ("File '%s' is not being currently processed, "
3578 		            "so the parent URN cannot be retrieved.", uri);
3579 		g_free (uri);
3580 
3581 		return NULL;
3582 	} else {
3583 		UpdateProcessingTaskContext *ctxt;
3584 
3585 		/* We are only storing the URN in the created/updated tasks */
3586 		ctxt = tracker_task_get_data (task);
3587 
3588 		if (!ctxt) {
3589 			gchar *uri;
3590 
3591 			uri = g_file_get_uri (file);
3592 			g_critical ("File '%s' is being processed, but not as a "
3593 			            "CREATED/UPDATED task, so cannot get parent "
3594 			            "URN",
3595 			            uri);
3596 			g_free (uri);
3597 			return NULL;
3598 		}
3599 
3600 		return ctxt->parent_urn;
3601 	}
3602 }
3603 
3604 void
3605 tracker_miner_fs_force_recheck (TrackerMinerFS *fs)
3606 {
3607 #if 0
3608 	GList *directories;
3609 
3610 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3611 
3612 	g_message ("Forcing re-check on all index directories");
3613 
3614 	directories = fs->priv->config_directories;
3615 
3616 	while (directories) {
3617 		tracker_priority_queue_add (fs->priv->directories,
3618 		                            directory_data_ref (directories->data),
3619 		                            G_PRIORITY_LOW);
3620 		directories = directories->next;
3621 	}
3622 
3623 	crawl_directories_start (fs);
3624 #endif
3625 }
3626 
3627 /**
3628  * tracker_miner_fs_set_mtime_checking:
3629  * @fs: a #TrackerMinerFS
3630  * @mtime_checking: a #gboolean
3631  *
3632  * Tells the miner-fs that during the crawling phase, directory mtime
3633  * checks should or shouldn't be performed against the database to
3634  * make sure we have the most up to date version of the file being
3635  * checked at the time. Setting this to #FALSE can dramatically
3636  * improve the start up the crawling of the @fs.
3637  *
3638  * The down side is that using this consistently means that some files
3639  * on the disk may be out of date with files in the database.
3640  *
3641  * The main purpose of this function is for systems where a @fs is
3642  * running the entire time and where it is very unlikely that a file
3643  * could be changed outside between startup and shutdown of the
3644  * process using this API.
3645  *
3646  * The default if not set directly is that @mtime_checking is #TRUE.
3647  *
3648  * Since: 0.10
3649  **/
3650 void
3651 tracker_miner_fs_set_mtime_checking (TrackerMinerFS *fs,
3652                                      gboolean        mtime_checking)
3653 {
3654 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3655 
3656 	fs->priv->mtime_checking = mtime_checking;
3657 }
3658 
3659 /**
3660  * tracker_miner_fs_get_mtime_checking:
3661  * @fs: a #TrackerMinerFS
3662  *
3663  * Returns: #TRUE if mtime checks for directories against the database
3664  * are done when @fs crawls the file system, otherwise #FALSE.
3665  *
3666  * Since: 0.10
3667  **/
3668 gboolean
3669 tracker_miner_fs_get_mtime_checking (TrackerMinerFS *fs)
3670 {
3671 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), FALSE);
3672 
3673 	return fs->priv->mtime_checking;
3674 }
3675 
3676 /**
3677  * tracker_miner_fs_force_mtime_checking:
3678  * @fs: a #TrackerMinerFS
3679  * @directory: a #GFile representing the directory
3680  *
3681  * Tells @fs to force mtime checking (regardless of the global mtime check
3682  * configuration) on the given @directory.
3683  *
3684  * Since: 0.12
3685  **/
3686 void
3687 tracker_miner_fs_force_mtime_checking (TrackerMinerFS *fs,
3688                                        GFile          *directory)
3689 {
3690 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3691 	g_return_if_fail (G_IS_FILE (directory));
3692 
3693 	tracker_indexing_tree_add (fs->priv->indexing_tree,
3694 	                           directory,
3695 	                           TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
3696 	                           TRACKER_DIRECTORY_FLAG_RECURSE |
3697 	                           TRACKER_DIRECTORY_FLAG_MONITOR);
3698 }
3699 
3700 void
3701 tracker_miner_fs_set_initial_crawling (TrackerMinerFS *fs,
3702                                        gboolean        do_initial_crawling)
3703 {
3704 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3705 
3706 	fs->priv->initial_crawling = do_initial_crawling;
3707 }
3708 
3709 gboolean
3710 tracker_miner_fs_get_initial_crawling (TrackerMinerFS *fs)
3711 {
3712 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), FALSE);
3713 
3714 	return fs->priv->initial_crawling;
3715 }
3716 
3717 /**
3718  * tracker_miner_fs_has_items_to_process:
3719  * @fs: a #TrackerMinerFS
3720  *
3721  * Returns: #TRUE if there are items to process in the internal
3722  * queues, otherwise #FALSE.
3723  *
3724  * Since: 0.10
3725  **/
3726 gboolean
3727 tracker_miner_fs_has_items_to_process (TrackerMinerFS *fs)
3728 {
3729 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), FALSE);
3730 
3731 	if (tracker_file_notifier_is_active (fs->priv->file_notifier) ||
3732 	    !tracker_priority_queue_is_empty (fs->priv->items_deleted) ||
3733 	    !tracker_priority_queue_is_empty (fs->priv->items_created) ||
3734 	    !tracker_priority_queue_is_empty (fs->priv->items_updated) ||
3735 	    !tracker_priority_queue_is_empty (fs->priv->items_moved) ||
3736 	    !tracker_priority_queue_is_empty (fs->priv->items_writeback)) {
3737 		return TRUE;
3738 	}
3739 
3740 	return FALSE;
3741 }
3742 
3743 /**
3744  * tracker_miner_fs_add_directory_without_parent:
3745  * @fs: a #TrackerMinerFS
3746  * @file: a #GFile
3747  *
3748  * Tells the miner-fs that the given #GFile corresponds to a
3749  * directory which was created in the store without a specific
3750  * parent object. In this case, when regenerating internal
3751  * caches, an extra query will be done so that these elements
3752  * are taken into account.
3753  *
3754  * Since: 0.10
3755  **/
3756 void
3757 tracker_miner_fs_add_directory_without_parent (TrackerMinerFS *fs,
3758                                                GFile          *file)
3759 {
3760 	g_return_if_fail (TRACKER_IS_MINER_FS (fs));
3761 	g_return_if_fail (G_IS_FILE (file));
3762 
3763 	tracker_indexing_tree_add (fs->priv->indexing_tree,
3764 	                           file,
3765 	                           TRACKER_DIRECTORY_FLAG_RECURSE |
3766 	                           TRACKER_DIRECTORY_FLAG_CHECK_MTIME |
3767 	                           TRACKER_DIRECTORY_FLAG_MONITOR |
3768 	                           TRACKER_DIRECTORY_FLAG_PRESERVE);
3769 }
3770 
3771 /**
3772  * tracker_miner_fs_get_indexing_tree:
3773  * @fs: a #TrackerMinerFS
3774  *
3775  * Returns the #TrackerIndexingTree which determines
3776  * what files/directories are indexed by @fs
3777  *
3778  * Returns: (transfer none): The #TrackerIndexingTree
3779  *          holding the indexing configuration
3780  **/
3781 TrackerIndexingTree *
3782 tracker_miner_fs_get_indexing_tree (TrackerMinerFS *fs)
3783 {
3784 	g_return_val_if_fail (TRACKER_IS_MINER_FS (fs), NULL);
3785 
3786 	return fs->priv->indexing_tree;
3787 }
3788 
3789 #ifdef EVENT_QUEUE_ENABLE_TRACE
3790 
3791 static void
3792 trace_files_foreach (gpointer file,
3793                      gpointer fs)
3794 {
3795 	gchar *uri;
3796 
3797 	uri = g_file_get_uri (G_FILE (file));
3798 	trace_eq ("(%s)     '%s'",
3799 	          G_OBJECT_TYPE_NAME (G_OBJECT (fs)),
3800 	          uri);
3801 	g_free (uri);
3802 }
3803 
3804 static void
3805 trace_moved_foreach (gpointer moved_data,
3806                      gpointer fs)
3807 {
3808 	ItemMovedData *data = moved_data;
3809 	gchar *source_uri;
3810 	gchar *dest_uri;
3811 
3812 	source_uri = g_file_get_uri (data->source_file);
3813 	dest_uri = g_file_get_uri (data->file);
3814 	trace_eq ("(%s)     '%s->%s'",
3815 	          G_OBJECT_TYPE_NAME (G_OBJECT (fs)),
3816 	          source_uri,
3817 	          dest_uri);
3818 	g_free (source_uri);
3819 	g_free (dest_uri);
3820 }
3821 
3822 static void
3823 trace_writeback_foreach (gpointer writeback_data,
3824                          gpointer fs)
3825 {
3826 	ItemWritebackData *data = writeback_data;
3827 	gchar *uri;
3828 
3829 	uri = g_file_get_uri (G_FILE (data->file));
3830 	trace_eq ("(%s)     '%s'",
3831 	          G_OBJECT_TYPE_NAME (G_OBJECT (fs)),
3832 	          uri);
3833 	g_free (uri);
3834 }
3835 
3836 static void
3837 miner_fs_trace_queue (TrackerMinerFS       *fs,
3838                       const gchar          *queue_name,
3839                       TrackerPriorityQueue *queue,
3840                       GFunc                 foreach_cb)
3841 {
3842 	trace_eq ("(%s) Queue '%s' has %u elements:",
3843 	          G_OBJECT_TYPE_NAME (fs),
3844 	          queue_name,
3845 	          tracker_priority_queue_get_length (queue));
3846 	tracker_priority_queue_foreach (queue,
3847 	                                foreach_cb,
3848 	                                fs);
3849 }
3850 
3851 static gboolean
3852 miner_fs_queues_status_trace_timeout_cb (gpointer data)
3853 {
3854 	TrackerMinerFS *fs = data;
3855 
3856 	trace_eq ("(%s) ------------", G_OBJECT_TYPE_NAME (fs));
3857 	miner_fs_trace_queue (fs, "CREATED",   fs->priv->items_created,   trace_files_foreach);
3858 	miner_fs_trace_queue (fs, "UPDATED",   fs->priv->items_updated,   trace_files_foreach);
3859 	miner_fs_trace_queue (fs, "DELETED",   fs->priv->items_deleted,   trace_files_foreach);
3860 	miner_fs_trace_queue (fs, "MOVED",     fs->priv->items_moved,     trace_moved_foreach);
3861 	miner_fs_trace_queue (fs, "WRITEBACK", fs->priv->items_writeback, trace_writeback_foreach);
3862 
3863 	return TRUE;
3864 }
3865 
3866 #endif /* EVENT_QUEUE_ENABLE_TRACE */