No issues found
1 /*
2 * Copyright (C) 2011, 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 * Author: Carlos Garnacho <carlos@lanedo.com>
20 */
21
22 #include <libtracker-common/tracker-file-utils.h>
23 #include "tracker-indexing-tree.h"
24
25 /**
26 * SECTION:tracker-indexing-tree
27 * @short_description: Indexing tree handling
28 *
29 * #TrackerIndexingTree handles the tree of directories configured to be indexed
30 * by the #TrackerMinerFS.
31 **/
32
33 G_DEFINE_TYPE (TrackerIndexingTree, tracker_indexing_tree, G_TYPE_OBJECT)
34
35 typedef struct _TrackerIndexingTreePrivate TrackerIndexingTreePrivate;
36 typedef struct _NodeData NodeData;
37 typedef struct _PatternData PatternData;
38 typedef struct _FindNodeData FindNodeData;
39
40 struct _NodeData
41 {
42 GFile *file;
43 guint flags : 7;
44 guint shallow : 1;
45 };
46
47 struct _PatternData
48 {
49 GPatternSpec *pattern;
50 TrackerFilterType type;
51 GFile *file; /* Only filled in in absolute paths */
52 };
53
54 struct _FindNodeData
55 {
56 GEqualFunc func;
57 GNode *node;
58 GFile *file;
59 };
60
61 struct _TrackerIndexingTreePrivate
62 {
63 GNode *config_tree;
64 GList *filter_patterns;
65 TrackerFilterPolicy policies[TRACKER_FILTER_PARENT_DIRECTORY + 1];
66
67 guint filter_hidden : 1;
68 };
69
70 enum {
71 PROP_0,
72 PROP_FILTER_HIDDEN
73 };
74
75 enum {
76 DIRECTORY_ADDED,
77 DIRECTORY_REMOVED,
78 DIRECTORY_UPDATED,
79 LAST_SIGNAL
80 };
81
82 static guint signals[LAST_SIGNAL] = { 0 };
83
84 static NodeData *
85 node_data_new (GFile *file,
86 guint flags)
87 {
88 NodeData *data;
89
90 data = g_slice_new (NodeData);
91 data->file = g_object_ref (file);
92 data->flags = flags;
93 data->shallow = FALSE;
94
95 return data;
96 }
97
98 static void
99 node_data_free (NodeData *data)
100 {
101 g_object_unref (data->file);
102 g_slice_free (NodeData, data);
103 }
104
105 static gboolean
106 node_free (GNode *node,
107 gpointer user_data)
108 {
109 node_data_free (node->data);
110 return FALSE;
111 }
112
113 static PatternData *
114 pattern_data_new (const gchar *glob_string,
115 guint type)
116 {
117 PatternData *data;
118
119 data = g_slice_new0 (PatternData);
120 data->pattern = g_pattern_spec_new (glob_string);
121 data->type = type;
122
123 if (g_path_is_absolute (glob_string)) {
124 data->file = g_file_new_for_path (glob_string);
125 }
126
127 return data;
128 }
129
130 static void
131 pattern_data_free (PatternData *data)
132 {
133 if (data->file) {
134 g_object_unref (data->file);
135 }
136
137 g_pattern_spec_free (data->pattern);
138 g_slice_free (PatternData, data);
139 }
140
141 static void
142 tracker_indexing_tree_get_property (GObject *object,
143 guint prop_id,
144 GValue *value,
145 GParamSpec *pspec)
146 {
147 TrackerIndexingTreePrivate *priv;
148
149 priv = TRACKER_INDEXING_TREE (object)->priv;
150
151 switch (prop_id) {
152 case PROP_FILTER_HIDDEN:
153 g_value_set_boolean (value, priv->filter_hidden);
154 break;
155 default:
156 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
157 break;
158 }
159 }
160
161 static void
162 tracker_indexing_tree_set_property (GObject *object,
163 guint prop_id,
164 const GValue *value,
165 GParamSpec *pspec)
166 {
167 TrackerIndexingTree *tree;
168
169 tree = TRACKER_INDEXING_TREE (object);
170
171 switch (prop_id) {
172 case PROP_FILTER_HIDDEN:
173 tracker_indexing_tree_set_filter_hidden (tree,
174 g_value_get_boolean (value));
175 break;
176 default:
177 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178 break;
179 }
180 }
181
182 static void
183 tracker_indexing_tree_finalize (GObject *object)
184 {
185 TrackerIndexingTreePrivate *priv;
186 TrackerIndexingTree *tree;
187
188 tree = TRACKER_INDEXING_TREE (object);
189 priv = tree->priv;
190
191 g_list_foreach (priv->filter_patterns, (GFunc) pattern_data_free, NULL);
192 g_list_free (priv->filter_patterns);
193
194 g_node_traverse (priv->config_tree,
195 G_POST_ORDER,
196 G_TRAVERSE_ALL,
197 -1,
198 (GNodeTraverseFunc) node_free,
199 NULL);
200 g_node_destroy (priv->config_tree);
201
202 G_OBJECT_CLASS (tracker_indexing_tree_parent_class)->finalize (object);
203 }
204
205 static void
206 tracker_indexing_tree_class_init (TrackerIndexingTreeClass *klass)
207 {
208 GObjectClass *object_class = G_OBJECT_CLASS (klass);
209
210 object_class->finalize = tracker_indexing_tree_finalize;
211 object_class->set_property = tracker_indexing_tree_set_property;
212 object_class->get_property = tracker_indexing_tree_get_property;
213
214 g_object_class_install_property (object_class,
215 PROP_FILTER_HIDDEN,
216 g_param_spec_boolean ("filter-hidden",
217 "Filter hidden",
218 "Whether hidden resources are filtered",
219 FALSE,
220 G_PARAM_READWRITE));
221 signals[DIRECTORY_ADDED] =
222 g_signal_new ("directory-added",
223 G_OBJECT_CLASS_TYPE (object_class),
224 G_SIGNAL_RUN_LAST,
225 G_STRUCT_OFFSET (TrackerIndexingTreeClass,
226 directory_added),
227 NULL, NULL,
228 g_cclosure_marshal_VOID__OBJECT,
229 G_TYPE_NONE, 1, G_TYPE_FILE);
230 signals[DIRECTORY_REMOVED] =
231 g_signal_new ("directory-removed",
232 G_OBJECT_CLASS_TYPE (object_class),
233 G_SIGNAL_RUN_LAST,
234 G_STRUCT_OFFSET (TrackerIndexingTreeClass,
235 directory_removed),
236 NULL, NULL,
237 g_cclosure_marshal_VOID__OBJECT,
238 G_TYPE_NONE, 1, G_TYPE_FILE);
239 signals[DIRECTORY_UPDATED] =
240 g_signal_new ("directory-updated",
241 G_OBJECT_CLASS_TYPE (object_class),
242 G_SIGNAL_RUN_LAST,
243 G_STRUCT_OFFSET (TrackerIndexingTreeClass,
244 directory_updated),
245 NULL, NULL,
246 g_cclosure_marshal_VOID__OBJECT,
247 G_TYPE_NONE, 1, G_TYPE_FILE);
248
249 g_type_class_add_private (object_class,
250 sizeof (TrackerIndexingTreePrivate));
251 }
252
253 static void
254 tracker_indexing_tree_init (TrackerIndexingTree *tree)
255 {
256 TrackerIndexingTreePrivate *priv;
257 NodeData *data;
258 GFile *root;
259 gint i;
260
261 priv = tree->priv = G_TYPE_INSTANCE_GET_PRIVATE (tree,
262 TRACKER_TYPE_INDEXING_TREE,
263 TrackerIndexingTreePrivate);
264 /* Add a shallow root node */
265 root = g_file_new_for_uri ("file:///");
266 data = node_data_new (root, 0);
267 data->shallow = TRUE;
268
269 priv->config_tree = g_node_new (data);
270 g_object_unref (root);
271
272 for (i = TRACKER_FILTER_FILE; i <= TRACKER_FILTER_PARENT_DIRECTORY; i++) {
273 priv->policies[i] = TRACKER_FILTER_POLICY_ACCEPT;
274 }
275 }
276
277 /**
278 * tracker_indexing_tree_new:
279 *
280 * Returns a newly created #TrackerIndexingTree
281 *
282 * Returns: a newly allocated #TrackerIndexingTree
283 **/
284 TrackerIndexingTree *
285 tracker_indexing_tree_new (void)
286 {
287 return g_object_new (TRACKER_TYPE_INDEXING_TREE, NULL);
288 }
289
290 #ifdef PRINT_INDEXING_TREE
291 static gboolean
292 print_node_foreach (GNode *node,
293 gpointer user_data)
294 {
295 NodeData *node_data = node->data;
296 gchar *uri;
297
298 uri = g_file_get_uri (node_data->file);
299 g_debug ("%*s %s", g_node_depth (node), "-", uri);
300 g_free (uri);
301
302 return FALSE;
303 }
304
305 static void
306 print_tree (GNode *node)
307 {
308 g_debug ("Printing modified tree...");
309 g_node_traverse (node,
310 G_PRE_ORDER,
311 G_TRAVERSE_ALL,
312 -1,
313 print_node_foreach,
314 NULL);
315 }
316
317 #endif /* PRINT_INDEXING_TREE */
318
319 static gboolean
320 find_node_foreach (GNode *node,
321 gpointer user_data)
322 {
323 FindNodeData *data = user_data;
324 NodeData *node_data = node->data;
325
326 if ((data->func) (data->file, node_data->file)) {
327 data->node = node;
328 return TRUE;
329 }
330
331 return FALSE;
332 }
333
334 static GNode *
335 find_directory_node (GNode *node,
336 GFile *file,
337 GEqualFunc func)
338 {
339 FindNodeData data;
340
341 data.file = file;
342 data.node = NULL;
343 data.func = func;
344
345 g_node_traverse (node,
346 G_POST_ORDER,
347 G_TRAVERSE_ALL,
348 -1,
349 find_node_foreach,
350 &data);
351
352 return data.node;
353 }
354
355 static void
356 check_reparent_node (GNode *node,
357 gpointer user_data)
358 {
359 GNode *new_node = user_data;
360 NodeData *new_node_data, *node_data;
361
362 new_node_data = new_node->data;
363 node_data = node->data;
364
365 if (g_file_has_prefix (node_data->file,
366 new_node_data->file)) {
367 g_node_unlink (node);
368 g_node_append (new_node, node);
369 }
370 }
371
372 /**
373 * tracker_indexing_tree_add:
374 * @tree: a #TrackerIndexingTree
375 * @directory: #GFile pointing to a directory
376 * @flags: Configuration flags for the directory
377 *
378 * Adds a directory to the indexing tree with the
379 * given configuration flags.
380 **/
381 void
382 tracker_indexing_tree_add (TrackerIndexingTree *tree,
383 GFile *directory,
384 TrackerDirectoryFlags flags)
385 {
386 TrackerIndexingTreePrivate *priv;
387 GNode *parent, *node;
388 NodeData *data;
389
390 g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
391 g_return_if_fail (G_IS_FILE (directory));
392
393 priv = tree->priv;
394 node = find_directory_node (priv->config_tree, directory,
395 (GEqualFunc) g_file_equal);
396
397 if (node) {
398 /* Node already existed */
399 data = node->data;
400 data->shallow = FALSE;
401
402 /* Overwrite flags if they are different */
403 if (data->flags != flags) {
404 gchar *uri;
405
406 uri = g_file_get_uri (directory);
407 g_message ("Overwriting flags for directory '%s'", uri);
408 g_free (uri);
409
410 data->flags = flags;
411 g_signal_emit (tree, signals[DIRECTORY_UPDATED], 0,
412 data->file);
413 }
414 return;
415 }
416
417 /* Find out the parent */
418 parent = find_directory_node (priv->config_tree, directory,
419 (GEqualFunc) g_file_has_prefix);
420
421 /* Create node, move children of parent that
422 * could be children of this new node now.
423 */
424 data = node_data_new (directory, flags);
425 node = g_node_new (data);
426
427 g_node_children_foreach (parent, G_TRAVERSE_ALL,
428 check_reparent_node, node);
429
430 /* Add the new node underneath the parent */
431 g_node_append (parent, node);
432
433 g_signal_emit (tree, signals[DIRECTORY_ADDED], 0, directory);
434
435 #ifdef PRINT_INDEXING_TREE
436 /* Print tree */
437 print_tree (priv->config_tree);
438 #endif /* PRINT_INDEXING_TREE */
439 }
440
441 /**
442 * tracker_indexing_tree_remove:
443 * @tree: a #TrackerIndexingTree
444 * @directory: #GFile pointing to a directory
445 *
446 * Removes @directory from the indexing tree, note that
447 * only directories previously added with tracker_indexing_tree_add()
448 * can be effectively removed.
449 **/
450 void
451 tracker_indexing_tree_remove (TrackerIndexingTree *tree,
452 GFile *directory)
453 {
454 TrackerIndexingTreePrivate *priv;
455 GNode *node, *parent;
456 NodeData *data;
457
458 g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
459 g_return_if_fail (G_IS_FILE (directory));
460
461 priv = tree->priv;
462 node = find_directory_node (priv->config_tree, directory,
463 (GEqualFunc) g_file_equal);
464 if (!node) {
465 return;
466 }
467
468 data = node->data;
469
470 if (!node->parent) {
471 /* Node is the config tree
472 * root, mark as shallow again
473 */
474 data->shallow = TRUE;
475 return;
476 }
477
478 g_signal_emit (tree, signals[DIRECTORY_REMOVED], 0, data->file);
479
480 parent = node->parent;
481 g_node_unlink (node);
482
483 /* Move children to parent */
484 g_node_children_foreach (node, G_TRAVERSE_ALL,
485 check_reparent_node, parent);
486
487 node_data_free (node->data);
488 g_node_destroy (node);
489 }
490
491 /**
492 * tracker_indexing_tree_add_filter:
493 * @tree: a #TrackerIndexingTree
494 * @filter: filter type
495 * @glob_string: glob-style string for the filter
496 *
497 * Adds a new filter for basenames.
498 **/
499 void
500 tracker_indexing_tree_add_filter (TrackerIndexingTree *tree,
501 TrackerFilterType filter,
502 const gchar *glob_string)
503 {
504 TrackerIndexingTreePrivate *priv;
505 PatternData *data;
506
507 g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
508 g_return_if_fail (glob_string != NULL);
509
510 priv = tree->priv;
511
512 data = pattern_data_new (glob_string, filter);
513 priv->filter_patterns = g_list_prepend (priv->filter_patterns, data);
514 }
515
516 /**
517 * tracker_indexing_tree_clear_filters:
518 * @tree: a #TrackerIndexingTree
519 * @type: filter type to clear
520 *
521 * Clears all filters of a given type.
522 **/
523 void
524 tracker_indexing_tree_clear_filters (TrackerIndexingTree *tree,
525 TrackerFilterType type)
526 {
527 TrackerIndexingTreePrivate *priv;
528 GList *l;
529
530 g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
531
532 priv = tree->priv;
533
534 for (l = priv->filter_patterns; l; l = l->next) {
535 PatternData *data = l->data;
536
537 if (data->type == type) {
538 /* When we delete the link 'l', we point back
539 * to the beginning of the list to make sure
540 * we don't miss anything.
541 */
542 l = priv->filter_patterns = g_list_delete_link (priv->filter_patterns, l);
543 pattern_data_free (data);
544 }
545 }
546 }
547
548 /**
549 * tracker_indexing_tree_file_matches_filter:
550 * @tree: a #TrackerIndexingTree
551 * @type: filter type
552 * @file: a #GFile
553 *
554 * Returns %TRUE if @file matches any filter of the given filter type.
555 *
556 * Returns: %TRUE if @file is filtered
557 **/
558 gboolean
559 tracker_indexing_tree_file_matches_filter (TrackerIndexingTree *tree,
560 TrackerFilterType type,
561 GFile *file)
562 {
563 TrackerIndexingTreePrivate *priv;
564 GList *filters;
565 gchar *basename;
566
567 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
568 g_return_val_if_fail (G_IS_FILE (file), FALSE);
569
570 priv = tree->priv;
571 filters = priv->filter_patterns;
572 basename = g_file_get_basename (file);
573
574 while (filters) {
575 PatternData *data = filters->data;
576
577 filters = filters->next;
578
579 if (data->type != type)
580 continue;
581
582 if (data->file &&
583 (g_file_equal (file, data->file) ||
584 g_file_has_prefix (file, data->file))) {
585 g_free (basename);
586 return TRUE;
587 }
588
589 if (g_pattern_match_string (data->pattern, basename)) {
590 g_free (basename);
591 return TRUE;
592 }
593 }
594
595 g_free (basename);
596 return FALSE;
597 }
598
599 static gboolean
600 parent_or_equals (GFile *file1,
601 GFile *file2)
602 {
603 return (file1 == file2 ||
604 g_file_equal (file1, file2) ||
605 g_file_has_prefix (file1, file2));
606 }
607
608 static gboolean
609 indexing_tree_file_is_filtered (TrackerIndexingTree *tree,
610 TrackerFilterType filter,
611 GFile *file)
612 {
613 TrackerIndexingTreePrivate *priv;
614
615 priv = tree->priv;
616
617 if (tracker_indexing_tree_file_matches_filter (tree, filter, file)) {
618 if (priv->policies[filter] == TRACKER_FILTER_POLICY_ACCEPT) {
619 /* Filter blocks otherwise accepted
620 * (by the default policy) file
621 */
622 return TRUE;
623 }
624 } else {
625 if (priv->policies[filter] == TRACKER_FILTER_POLICY_DENY) {
626 /* No match, and the default policy denies it */
627 return TRUE;
628 }
629 }
630
631 return FALSE;
632 }
633
634 /**
635 * tracker_indexing_tree_file_is_indexable:
636 * @tree: a #TrackerIndexingTree
637 * @file: a #GFile
638 * @file_type: a #GFileType
639 *
640 * returns %TRUE if @file should be indexed according to the
641 * parameters given through tracker_indexing_tree_add() and
642 * tracker_indexing_tree_add_filter().
643 *
644 * If @file_type is #G_FILE_TYPE_UNKNOWN, file type will be queried to the
645 * file system.
646 *
647 * Returns: %TRUE if @file should be indexed.
648 **/
649 gboolean
650 tracker_indexing_tree_file_is_indexable (TrackerIndexingTree *tree,
651 GFile *file,
652 GFileType file_type)
653 {
654 TrackerFilterType filter;
655 TrackerDirectoryFlags config_flags;
656 GFile *config_file;
657
658 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
659 g_return_val_if_fail (G_IS_FILE (file), FALSE);
660
661 if (file_type == G_FILE_TYPE_UNKNOWN)
662 file_type = g_file_query_file_type (file,
663 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
664 NULL);
665
666 filter = (file_type == G_FILE_TYPE_DIRECTORY) ?
667 TRACKER_FILTER_DIRECTORY : TRACKER_FILTER_FILE;
668
669 if (indexing_tree_file_is_filtered (tree, filter, file)) {
670 return FALSE;
671 }
672
673 config_file = tracker_indexing_tree_get_root (tree,file, &config_flags);
674 if (!config_file) {
675 /* Not under an added dir */
676 return FALSE;
677 }
678
679 if (config_flags & TRACKER_DIRECTORY_FLAG_IGNORE) {
680 return FALSE;
681 }
682
683 if (g_file_equal (file, config_file)) {
684 return TRUE;
685 } else {
686 if ((config_flags & TRACKER_DIRECTORY_FLAG_RECURSE) == 0 &&
687 !g_file_has_parent (file, config_file)) {
688 /* Non direct child in a non-recursive dir, ignore */
689 return FALSE;
690 }
691
692 if (tracker_indexing_tree_get_filter_hidden (tree) &&
693 tracker_file_is_hidden (file)) {
694 return FALSE;
695 }
696
697 return TRUE;
698 }
699 }
700
701 /**
702 * tracker_indexing_tree_parent_is_indexable:
703 * @tree: a #TrackerIndexingTree
704 * @parent: parent directory
705 * @children: (element-type GFile): children within @parent
706 *
707 * returns %TRUE if @parent should be indexed based on its contents.
708 *
709 * Returns: %TRUE if @parent should be indexed.
710 **/
711 gboolean
712 tracker_indexing_tree_parent_is_indexable (TrackerIndexingTree *tree,
713 GFile *parent,
714 GList *children)
715 {
716 if (!tracker_indexing_tree_file_is_indexable (tree,
717 parent,
718 G_FILE_TYPE_DIRECTORY)) {
719 return FALSE;
720 }
721
722 while (children) {
723 if (indexing_tree_file_is_filtered (tree,
724 TRACKER_FILTER_PARENT_DIRECTORY,
725 children->data)) {
726 return FALSE;
727 }
728
729 children = children->next;
730 }
731
732 return TRUE;
733 }
734
735 gboolean
736 tracker_indexing_tree_get_filter_hidden (TrackerIndexingTree *tree)
737 {
738 TrackerIndexingTreePrivate *priv;
739
740 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
741
742 priv = tree->priv;
743 return priv->filter_hidden;
744 }
745
746 void
747 tracker_indexing_tree_set_filter_hidden (TrackerIndexingTree *tree,
748 gboolean filter_hidden)
749 {
750 TrackerIndexingTreePrivate *priv;
751
752 g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
753
754 priv = tree->priv;
755 priv->filter_hidden = filter_hidden;
756
757 g_object_notify (G_OBJECT (tree), "filter-hidden");
758 }
759
760 void
761 tracker_indexing_tree_set_default_policy (TrackerIndexingTree *tree,
762 TrackerFilterType filter,
763 TrackerFilterPolicy policy)
764 {
765 TrackerIndexingTreePrivate *priv;
766
767 g_return_if_fail (TRACKER_IS_INDEXING_TREE (tree));
768 g_return_if_fail (filter >= TRACKER_FILTER_FILE && filter <= TRACKER_FILTER_PARENT_DIRECTORY);
769
770 priv = tree->priv;
771 priv->policies[filter] = policy;
772 }
773
774 TrackerFilterPolicy
775 tracker_indexing_tree_get_default_policy (TrackerIndexingTree *tree,
776 TrackerFilterType filter)
777 {
778 TrackerIndexingTreePrivate *priv;
779
780 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree),
781 TRACKER_FILTER_POLICY_DENY);
782 g_return_val_if_fail (filter >= TRACKER_FILTER_FILE &&
783 filter <= TRACKER_FILTER_PARENT_DIRECTORY,
784 TRACKER_FILTER_POLICY_DENY);
785
786 priv = tree->priv;
787 return priv->policies[filter];
788 }
789
790 /**
791 * tracker_indexing_tree_get_root:
792 * @tree: a #TrackerIndexingTree
793 * @file: a #GFile
794 * @directory_flags: (out): return location for the applying #TrackerDirectoryFlags
795 *
796 * Returns the #GFile that was previously added through tracker_indexing_tree_add()
797 * and would equal or contain @file, or %NULL if none applies.
798 *
799 * If the return value is non-%NULL, @directory_flags would contain the
800 * #TrackerDirectoryFlags applying to @file.
801 *
802 * Returns: (transfer none): the effective parent in @tree, or %NULL
803 **/
804 GFile *
805 tracker_indexing_tree_get_root (TrackerIndexingTree *tree,
806 GFile *file,
807 TrackerDirectoryFlags *directory_flags)
808 {
809 TrackerIndexingTreePrivate *priv;
810 NodeData *data;
811 GNode *parent;
812
813 if (directory_flags) {
814 *directory_flags = TRACKER_DIRECTORY_FLAG_NONE;
815 }
816
817 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), NULL);
818 g_return_val_if_fail (G_IS_FILE (file), NULL);
819
820 priv = tree->priv;
821 parent = find_directory_node (priv->config_tree, file,
822 (GEqualFunc) parent_or_equals);
823 if (!parent) {
824 return NULL;
825 }
826
827 data = parent->data;
828
829 if (!data->shallow &&
830 (file == data->file ||
831 g_file_equal (file, data->file) ||
832 g_file_has_prefix (file, data->file))) {
833 if (directory_flags) {
834 *directory_flags = data->flags;
835 }
836
837 return data->file;
838 }
839
840 return NULL;
841 }
842
843 gboolean
844 tracker_indexing_tree_file_is_root (TrackerIndexingTree *tree,
845 GFile *file)
846 {
847 TrackerIndexingTreePrivate *priv;
848 GNode *node;
849
850 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), FALSE);
851 g_return_val_if_fail (G_IS_FILE (file), FALSE);
852
853 priv = tree->priv;
854 node = find_directory_node (priv->config_tree, file,
855 (GEqualFunc) g_file_equal);
856 return node != NULL;
857 }
858
859 static gboolean
860 prepend_config_root (GNode *node,
861 gpointer user_data)
862 {
863 GList **list = user_data;
864 NodeData *data = node->data;
865
866 *list = g_list_prepend (*list, data->file);
867 return FALSE;
868 }
869
870 /**
871 * tracker_indexing_tree_list_roots:
872 * @tree: a #TrackerIndexingTree
873 *
874 * Returns the list of indexing roots in @tree
875 *
876 * Returns: (transfer container) (element-type GFile): The list
877 * of roots, the list itself must be freed with g_list_free(),
878 * the list elements are owned by @tree and should not be
879 * freed.
880 **/
881 GList *
882 tracker_indexing_tree_list_roots (TrackerIndexingTree *tree)
883 {
884 TrackerIndexingTreePrivate *priv;
885 GList *nodes = NULL;
886
887 g_return_val_if_fail (TRACKER_IS_INDEXING_TREE (tree), NULL);
888
889 priv = tree->priv;
890 g_node_traverse (priv->config_tree,
891 G_POST_ORDER,
892 G_TRAVERSE_ALL,
893 -1,
894 prepend_config_root,
895 &nodes);
896 return nodes;
897 }