1 /*
2 * Copyright (C) 2008, Nokia <ivan.frade@nokia.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU 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-miners.h"
21
22 #include <string.h>
23
24 #include <gio/gio.h>
25 #include <gio/gunixmounts.h>
26
27 #include <libtracker-miners-common/tracker-log.h>
28
29 #include "tracker-storage.h"
30
31 /**
32 * SECTION:tracker-storage
33 * @short_description: Removable storage and mount point convenience API
34 * @include: libtracker-miner/tracker-miner.h
35 *
36 * This API is a convenience to to be able to keep track of volumes
37 * which are mounted and also the type of removable media available.
38 * The API is built upon the top of GIO's #GMount, #GDrive and #GVolume API.
39 **/
40
41 #define TRACKER_STORAGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TRACKER_TYPE_STORAGE, TrackerStoragePrivate))
42
43 typedef struct {
44 GVolumeMonitor *volume_monitor;
45
46 GNode *mounts;
47 GHashTable *mounts_by_uuid;
48 GHashTable *unmount_watchdogs;
49 } TrackerStoragePrivate;
50
51 typedef struct {
52 gchar *mount_point;
53 gchar *uuid;
54 guint unmount_timer_id;
55 guint removable : 1;
56 guint optical : 1;
57 } MountInfo;
58
59 typedef struct {
60 const gchar *path;
61 GNode *node;
62 } TraverseData;
63
64 typedef struct {
65 GSList *roots;
66 TrackerStorageType type;
67 gboolean exact_match;
68 } GetRoots;
69
70 typedef struct {
71 TrackerStorage *storage;
72 GMount *mount;
73 } UnmountCheckData;
74
75 static void tracker_storage_finalize (GObject *object);
76 static gboolean mount_info_free (GNode *node,
77 gpointer user_data);
78 static void mount_node_free (GNode *node);
79 static gboolean mounts_setup (TrackerStorage *storage);
80 static void mount_added_cb (GVolumeMonitor *monitor,
81 GMount *mount,
82 gpointer user_data);
83 static void mount_removed_cb (GVolumeMonitor *monitor,
84 GMount *mount,
85 gpointer user_data);
86 static void mount_pre_removed_cb (GVolumeMonitor *monitor,
87 GMount *mount,
88 gpointer user_data);
89
90 enum {
91 MOUNT_POINT_ADDED,
92 MOUNT_POINT_REMOVED,
93 LAST_SIGNAL
94 };
95
96 static guint signals[LAST_SIGNAL] = {0};
97
98 G_DEFINE_TYPE_WITH_PRIVATE (TrackerStorage, tracker_storage, G_TYPE_OBJECT);
99
100 static void
tracker_storage_class_init(TrackerStorageClass * klass)101 tracker_storage_class_init (TrackerStorageClass *klass)
102 {
103 GObjectClass *object_class;
104
105 object_class = G_OBJECT_CLASS (klass);
106
107 object_class->finalize = tracker_storage_finalize;
108
109 signals[MOUNT_POINT_ADDED] =
110 g_signal_new ("mount-point-added",
111 G_TYPE_FROM_CLASS (klass),
112 G_SIGNAL_RUN_LAST,
113 0,
114 NULL, NULL,
115 NULL,
116 G_TYPE_NONE,
117 5,
118 G_TYPE_STRING,
119 G_TYPE_STRING,
120 G_TYPE_STRING,
121 G_TYPE_BOOLEAN,
122 G_TYPE_BOOLEAN);
123
124 signals[MOUNT_POINT_REMOVED] =
125 g_signal_new ("mount-point-removed",
126 G_TYPE_FROM_CLASS (klass),
127 G_SIGNAL_RUN_LAST,
128 0,
129 NULL, NULL,
130 NULL,
131 G_TYPE_NONE,
132 2,
133 G_TYPE_STRING,
134 G_TYPE_STRING);
135 }
136
137 static void
tracker_storage_init(TrackerStorage * storage)138 tracker_storage_init (TrackerStorage *storage)
139 {
140 TrackerStoragePrivate *priv;
141
142 g_message ("Initializing Storage...");
143
144 priv = tracker_storage_get_instance_private (storage);
145
146 priv->mounts = g_node_new (NULL);
147
148 priv->mounts_by_uuid = g_hash_table_new_full (g_str_hash,
149 g_str_equal,
150 (GDestroyNotify) g_free,
151 NULL);
152 priv->unmount_watchdogs = g_hash_table_new_full (NULL, NULL, NULL,
153 (GDestroyNotify) g_source_remove);
154
155 priv->volume_monitor = g_volume_monitor_get ();
156
157 /* Volume and property notification callbacks */
158 g_signal_connect_object (priv->volume_monitor, "mount-removed",
159 G_CALLBACK (mount_removed_cb), storage, 0);
160 g_signal_connect_object (priv->volume_monitor, "mount-pre-unmount",
161 G_CALLBACK (mount_pre_removed_cb), storage, 0);
162 g_signal_connect_object (priv->volume_monitor, "mount-added",
163 G_CALLBACK (mount_added_cb), storage, 0);
164
165 g_message ("Mount monitors set up for to watch for added, removed and pre-unmounts...");
166
167 /* Get all mounts and set them up */
168 if (!mounts_setup (storage)) {
169 return;
170 }
171 }
172
173 static void
tracker_storage_finalize(GObject * object)174 tracker_storage_finalize (GObject *object)
175 {
176 TrackerStoragePrivate *priv;
177
178 priv = tracker_storage_get_instance_private (TRACKER_STORAGE (object));
179
180 g_hash_table_destroy (priv->unmount_watchdogs);
181
182 if (priv->mounts_by_uuid) {
183 g_hash_table_unref (priv->mounts_by_uuid);
184 }
185
186 if (priv->mounts) {
187 mount_node_free (priv->mounts);
188 }
189
190 if (priv->volume_monitor) {
191 g_object_unref (priv->volume_monitor);
192 }
193
194 (G_OBJECT_CLASS (tracker_storage_parent_class)->finalize) (object);
195 }
196
197 static void
mount_node_free(GNode * node)198 mount_node_free (GNode *node)
199 {
200 g_node_traverse (node,
201 G_POST_ORDER,
202 G_TRAVERSE_ALL,
203 -1,
204 mount_info_free,
205 NULL);
206
207 g_node_destroy (node);
208 }
209
210 static gboolean
mount_node_traverse_func(GNode * node,gpointer user_data)211 mount_node_traverse_func (GNode *node,
212 gpointer user_data)
213 {
214 TraverseData *data;
215 MountInfo *info;
216
217 if (!node->data) {
218 /* Root node */
219 return FALSE;
220 }
221
222 data = user_data;
223 info = node->data;
224
225 if (g_str_has_prefix (data->path, info->mount_point)) {
226 data->node = node;
227 return TRUE;
228 }
229
230 return FALSE;
231 }
232
233 static GNode *
mount_node_find(GNode * root,const gchar * path)234 mount_node_find (GNode *root,
235 const gchar *path)
236 {
237 TraverseData data = { path, NULL };
238
239 g_node_traverse (root,
240 G_POST_ORDER,
241 G_TRAVERSE_ALL,
242 -1,
243 mount_node_traverse_func,
244 &data);
245
246 return data.node;
247 }
248
249 static gboolean
mount_info_free(GNode * node,gpointer user_data)250 mount_info_free (GNode *node,
251 gpointer user_data)
252 {
253 MountInfo *info;
254
255 info = node->data;
256
257 if (info) {
258 g_free (info->mount_point);
259 g_free (info->uuid);
260
261 g_slice_free (MountInfo, info);
262 }
263
264 return FALSE;
265 }
266
267 static MountInfo *
mount_info_find(GNode * root,const gchar * path)268 mount_info_find (GNode *root,
269 const gchar *path)
270 {
271 GNode *node;
272
273 node = mount_node_find (root, path);
274 return (node) ? node->data : NULL;
275 }
276
277 static TrackerStorageType
mount_info_get_type(MountInfo * info)278 mount_info_get_type (MountInfo *info)
279 {
280 TrackerStorageType mount_type = 0;
281
282 if (info->removable) {
283 mount_type |= TRACKER_STORAGE_REMOVABLE;
284 }
285
286 if (info->optical) {
287 mount_type |= TRACKER_STORAGE_OPTICAL;
288 }
289
290 return mount_type;
291 }
292
293 static gchar *
mount_point_normalize(const gchar * mount_point)294 mount_point_normalize (const gchar *mount_point)
295 {
296 gchar *mp;
297
298 /* Normalize all mount points to have a / at the end */
299 if (g_str_has_suffix (mount_point, G_DIR_SEPARATOR_S)) {
300 mp = g_strdup (mount_point);
301 } else {
302 mp = g_strconcat (mount_point, G_DIR_SEPARATOR_S, NULL);
303 }
304
305 return mp;
306 }
307
308 static GNode *
mount_add_hierarchy(GNode * root,const gchar * uuid,const gchar * mount_point,gboolean removable,gboolean optical)309 mount_add_hierarchy (GNode *root,
310 const gchar *uuid,
311 const gchar *mount_point,
312 gboolean removable,
313 gboolean optical)
314 {
315 MountInfo *info;
316 GNode *node;
317 gchar *mp;
318
319 mp = mount_point_normalize (mount_point);
320 node = mount_node_find (root, mp);
321
322 if (!node) {
323 node = root;
324 }
325
326 info = g_slice_new (MountInfo);
327 info->mount_point = mp;
328 info->uuid = g_strdup (uuid);
329 info->removable = removable;
330 info->optical = optical;
331
332 return g_node_append_data (node, info);
333 }
334
335 static void
mount_add_new(TrackerStorage * storage,const gchar * uuid,const gchar * mount_point,const gchar * mount_name,gboolean removable_device,gboolean optical_disc)336 mount_add_new (TrackerStorage *storage,
337 const gchar *uuid,
338 const gchar *mount_point,
339 const gchar *mount_name,
340 gboolean removable_device,
341 gboolean optical_disc)
342 {
343 TrackerStoragePrivate *priv;
344 GNode *node;
345
346 priv = tracker_storage_get_instance_private (storage);
347
348 node = mount_add_hierarchy (priv->mounts, uuid, mount_point, removable_device, optical_disc);
349 g_hash_table_insert (priv->mounts_by_uuid, g_strdup (uuid), node);
350
351 g_signal_emit (storage,
352 signals[MOUNT_POINT_ADDED],
353 0,
354 uuid,
355 mount_point,
356 mount_name,
357 removable_device,
358 optical_disc,
359 NULL);
360 }
361
362 static gchar *
mount_guess_content_type(GMount * mount,gboolean * is_optical,gboolean * is_multimedia,gboolean * is_blank)363 mount_guess_content_type (GMount *mount,
364 gboolean *is_optical,
365 gboolean *is_multimedia,
366 gboolean *is_blank)
367 {
368 gchar *content_type = NULL;
369 gchar **guess_type;
370
371 *is_optical = FALSE;
372 *is_multimedia = FALSE;
373 *is_blank = FALSE;
374
375 /* This function has 2 purposes:
376 *
377 * 1. Detect if we are using optical media
378 * 2. Detect if we are video or music, we can't index those types
379 *
380 * We try to determine the content type because we don't want
381 * to store Volume information in Tracker about DVDs and media
382 * which has no real data for us to mine.
383 *
384 * Generally, if is_multimedia is TRUE then we end up ignoring
385 * the media.
386 */
387 guess_type = g_mount_guess_content_type_sync (mount, TRUE, NULL, NULL);
388
389 if (guess_type) {
390 gint i = 0;
391
392 while (!content_type && guess_type[i]) {
393 if (!g_strcmp0 (guess_type[i], "x-content/image-picturecd")) {
394 /* Images */
395 content_type = g_strdup (guess_type[i]);
396 } else if (!g_strcmp0 (guess_type[i], "x-content/video-bluray") ||
397 !g_strcmp0 (guess_type[i], "x-content/video-dvd") ||
398 !g_strcmp0 (guess_type[i], "x-content/video-hddvd") ||
399 !g_strcmp0 (guess_type[i], "x-content/video-svcd") ||
400 !g_strcmp0 (guess_type[i], "x-content/video-vcd")) {
401 /* Videos */
402 *is_multimedia = TRUE;
403 content_type = g_strdup (guess_type[i]);
404 } else if (!g_strcmp0 (guess_type[i], "x-content/audio-cdda") ||
405 !g_strcmp0 (guess_type[i], "x-content/audio-dvd") ||
406 !g_strcmp0 (guess_type[i], "x-content/audio-player")) {
407 /* Audios */
408 *is_multimedia = TRUE;
409 content_type = g_strdup (guess_type[i]);
410 } else if (!g_strcmp0 (guess_type[i], "x-content/blank-bd") ||
411 !g_strcmp0 (guess_type[i], "x-content/blank-cd") ||
412 !g_strcmp0 (guess_type[i], "x-content/blank-dvd") ||
413 !g_strcmp0 (guess_type[i], "x-content/blank-hddvd")) {
414 /* Blank */
415 *is_blank = TRUE;
416 content_type = g_strdup (guess_type[i]);
417 } else if (!g_strcmp0 (guess_type[i], "x-content/software") ||
418 !g_strcmp0 (guess_type[i], "x-content/unix-software") ||
419 !g_strcmp0 (guess_type[i], "x-content/win32-software")) {
420 /* NOTE: This one is a guess, can we
421 * have this content type on
422 * none-optical mount points?
423 */
424 content_type = g_strdup (guess_type[i]);
425 } else {
426 /* else, keep on with the next guess, if any */
427 i++;
428 }
429 }
430
431 /* If we didn't have an exact match on possible guessed content types,
432 * then use the first one returned (best guess always first) if any */
433 if (!content_type && guess_type[0]) {
434 content_type = g_strdup (guess_type[0]);
435 }
436
437 g_strfreev (guess_type);
438 }
439
440 if (content_type) {
441 if (strstr (content_type, "vcd") ||
442 strstr (content_type, "cdda") ||
443 strstr (content_type, "dvd") ||
444 strstr (content_type, "bluray")) {
445 *is_optical = TRUE;
446 }
447 } else {
448 GUnixMountEntry *entry;
449 gchar *mount_path;
450 GFile *mount_root;
451
452 /* No content type was guessed, try to find out
453 * at least whether it's an optical media or not
454 */
455 mount_root = g_mount_get_root (mount);
456 mount_path = g_file_get_path (mount_root);
457
458 /* FIXME: Try to assume we have a unix mount :(
459 * EEK, once in a while, I have to write crack, oh well
460 */
461 if (mount_path &&
462 (entry = g_unix_mount_at (mount_path, NULL)) != NULL) {
463 const gchar *filesystem_type;
464 gchar *device_path = NULL;
465 GVolume *volume;
466
467 volume = g_mount_get_volume (mount);
468 filesystem_type = g_unix_mount_get_fs_type (entry);
469 g_debug (" Using filesystem type:'%s'",
470 filesystem_type);
471
472 /* Volume may be NULL */
473 if (volume) {
474 device_path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
475 g_debug (" Using device path:'%s'",
476 device_path);
477 g_object_unref (volume);
478 }
479
480 /* NOTE: This code was taken from guess_mount_type()
481 * in GIO's gunixmounts.c and adapted purely for
482 * guessing optical media. We don't use the guessing
483 * code for other types such as MEMSTICKS, ZIPs,
484 * IPODs, etc.
485 *
486 * This code may need updating over time since it is
487 * very situational depending on how distributions
488 * mount their devices and how devices are named in
489 * /dev.
490 */
491 if (strcmp (filesystem_type, "udf") == 0 ||
492 strcmp (filesystem_type, "iso9660") == 0 ||
493 strcmp (filesystem_type, "cd9660") == 0 ||
494 (device_path &&
495 (g_str_has_prefix (device_path, "/dev/cdrom") ||
496 g_str_has_prefix (device_path, "/dev/acd") ||
497 g_str_has_prefix (device_path, "/dev/cd")))) {
498 *is_optical = TRUE;
499 } else if (device_path &&
500 g_str_has_prefix (device_path, "/vol/")) {
501 const gchar *name;
502
503 name = mount_path + strlen ("/");
504
505 if (g_str_has_prefix (name, "cdrom")) {
506 *is_optical = TRUE;
507 }
508 } else {
509 gchar *basename = g_path_get_basename (mount_path);
510
511 if (g_str_has_prefix (basename, "cdr") ||
512 g_str_has_prefix (basename, "cdwriter") ||
513 g_str_has_prefix (basename, "burn") ||
514 g_str_has_prefix (basename, "dvdr")) {
515 *is_optical = TRUE;
516 }
517
518 g_free (basename);
519 }
520
521 g_free (device_path);
522 g_free (mount_path);
523 g_unix_mount_free (entry);
524 } else {
525 g_debug (" No GUnixMountEntry found, needed for detecting if optical media... :(");
526 g_free (mount_path);
527 }
528
529 g_object_unref (mount_root);
530 }
531
532 return content_type;
533 }
534
535 static void
mount_add(TrackerStorage * storage,GMount * mount)536 mount_add (TrackerStorage *storage,
537 GMount *mount)
538 {
539 TrackerStoragePrivate *priv;
540 GFile *root;
541 GVolume *volume;
542 gchar *mount_name, *mount_path, *uuid;
543 gboolean is_optical = FALSE;
544 gboolean is_removable = FALSE;
545
546 /* Get mount name */
547 mount_name = g_mount_get_name (mount);
548
549 /* Get root path of the mount */
550 root = g_mount_get_root (mount);
551 if (!g_file_is_native (root)) {
552 gchar *uri = g_file_get_uri (root);
553
554 g_debug ("Ignoring mount '%s', URI '%s' is not native",
555 mount_name, uri);
556 g_free (uri);
557 return;
558 }
559
560 mount_path = g_file_get_path (root);
561 g_debug ("Found '%s' mounted on path '%s'",
562 mount_name,
563 mount_path);
564
565 /* Do not process shadowed mounts! */
566 if (g_mount_is_shadowed (mount)) {
567 g_debug (" Skipping shadowed mount '%s'", mount_name);
568 g_object_unref (root);
569 g_free (mount_path);
570 g_free (mount_name);
571 return;
572 }
573
574 priv = tracker_storage_get_instance_private (storage);
575
576 /* fstab partitions may not have corresponding
577 * GVolumes, so volume may be NULL */
578 volume = g_mount_get_volume (mount);
579 if (volume) {
580 /* GMount with GVolume */
581
582 /* Try to get UUID from the Volume.
583 * Note that g_volume_get_uuid() is NOT equivalent */
584 uuid = g_volume_get_identifier (volume,
585 G_VOLUME_IDENTIFIER_KIND_UUID);
586 if (!uuid) {
587 gchar *content_type;
588 gboolean is_multimedia;
589 gboolean is_blank;
590
591 /* Optical discs usually won't have UUID in the GVolume */
592 content_type = mount_guess_content_type (mount, &is_optical, &is_multimedia, &is_blank);
593 is_removable = TRUE;
594
595 /* We don't index content which is video, music or blank */
596 if (!is_multimedia && !is_blank) {
597 uuid = g_compute_checksum_for_string (G_CHECKSUM_MD5,
598 mount_name,
599 -1);
600 g_debug (" No UUID, generated:'%s' (based on mount name)", uuid);
601 g_debug (" Assuming GVolume has removable media, if wrong report a bug! "
602 "content type is '%s'",
603 content_type);
604 } else {
605 g_debug (" Being ignored because mount with volume is music/video/blank "
606 "(content type:%s, optical:%s, multimedia:%s, blank:%s)",
607 content_type,
608 is_optical ? "yes" : "no",
609 is_multimedia ? "yes" : "no",
610 is_blank ? "yes" : "no");
611 }
612
613 g_free (content_type);
614 } else {
615 /* Any other removable media will have UUID in the
616 * GVolume. Note that this also may include some
617 * partitions in the machine which have GVolumes
618 * associated to the GMounts. We also check a drive
619 * exists to be sure the device is local. */
620 GDrive *drive;
621
622 drive = g_volume_get_drive (volume);
623
624 if (drive) {
625 /* We can't mount/unmount system volumes, so tag
626 * them as non removable. */
627 is_removable = g_volume_can_mount (volume);
628 g_debug (" Found mount with volume and drive which %s be mounted: "
629 "Assuming it's %s removable, if wrong report a bug!",
630 is_removable ? "can" : "cannot",
631 is_removable ? "" : "not");
632 g_object_unref (drive);
633 } else {
634 /* Note: not sure when this can happen... */
635 g_debug (" Mount with volume but no drive, "
636 "assuming not a removable device, "
637 "if wrong report a bug!");
638 is_removable = FALSE;
639 }
640 }
641
642 g_object_unref (volume);
643 } else {
644 /* GMount without GVolume.
645 * Note: Never found a case where this g_mount_get_uuid() returns
646 * non-NULL... :-) */
647 uuid = g_mount_get_uuid (mount);
648 if (!uuid) {
649 if (mount_path) {
650 gchar *content_type;
651 gboolean is_multimedia;
652 gboolean is_blank;
653
654 content_type = mount_guess_content_type (mount, &is_optical, &is_multimedia, &is_blank);
655
656 /* Note: for GMounts without GVolume, is_blank should NOT be considered,
657 * as it may give unwanted results... */
658 if (!is_multimedia) {
659 uuid = g_compute_checksum_for_string (G_CHECKSUM_MD5,
660 mount_path,
661 -1);
662 g_debug (" No UUID, generated:'%s' (based on mount path)", uuid);
663 } else {
664 g_debug (" Being ignored because mount is music/video "
665 "(content type:%s, optical:%s, multimedia:%s)",
666 content_type,
667 is_optical ? "yes" : "no",
668 is_multimedia ? "yes" : "no");
669 }
670
671 g_free (content_type);
672 } else {
673 g_debug (" Being ignored because mount has no GVolume (i.e. not user mountable) "
674 "and has no mount root path available");
675 }
676 }
677 }
678
679 /* If we got something to be used as UUID, then add the mount
680 * to the TrackerStorage */
681 if (uuid && mount_path && !g_hash_table_lookup (priv->mounts_by_uuid, uuid)) {
682 g_debug (" Adding mount point with UUID: '%s', removable: %s, optical: %s, path: '%s'",
683 uuid,
684 is_removable ? "yes" : "no",
685 is_optical ? "yes" : "no",
686 mount_path);
687 mount_add_new (storage, uuid, mount_path, mount_name, is_removable, is_optical);
688 } else {
689 g_debug (" Skipping mount point with UUID: '%s', path: '%s', already managed: '%s'",
690 uuid ? uuid : "none",
691 mount_path ? mount_path : "none",
692 (uuid && g_hash_table_lookup (priv->mounts_by_uuid, uuid)) ? "yes" : "no");
693 }
694
695 g_free (mount_name);
696 g_free (mount_path);
697 g_free (uuid);
698 g_object_unref (root);
699 }
700
701 static gboolean
mounts_setup(TrackerStorage * storage)702 mounts_setup (TrackerStorage *storage)
703 {
704 TrackerStoragePrivate *priv;
705 GList *mounts, *lm;
706
707 priv = tracker_storage_get_instance_private (storage);
708
709 mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
710
711 if (!mounts) {
712 g_message ("No mounts found to iterate");
713 return TRUE;
714 }
715
716 /* Iterate over all available mounts and add them.
717 * Note that GVolumeMonitor shows only those mounts which are
718 * actually mounted. */
719 for (lm = mounts; lm; lm = g_list_next (lm)) {
720 mount_add (storage, lm->data);
721 g_object_unref (lm->data);
722 }
723
724 g_list_free (mounts);
725
726 return TRUE;
727 }
728
729 static void
mount_added_cb(GVolumeMonitor * monitor,GMount * mount,gpointer user_data)730 mount_added_cb (GVolumeMonitor *monitor,
731 GMount *mount,
732 gpointer user_data)
733 {
734 mount_add (user_data, mount);
735 }
736
737 static void
mount_remove(TrackerStorage * storage,GMount * mount)738 mount_remove (TrackerStorage *storage,
739 GMount *mount)
740 {
741 TrackerStoragePrivate *priv;
742 MountInfo *info;
743 GNode *node;
744 GFile *file;
745 gchar *name;
746 gchar *mount_point;
747 gchar *mp;
748
749 priv = tracker_storage_get_instance_private (storage);
750
751 file = g_mount_get_root (mount);
752 mount_point = g_file_get_path (file);
753 name = g_mount_get_name (mount);
754
755 mp = mount_point_normalize (mount_point);
756 node = mount_node_find (priv->mounts, mp);
757 g_free (mp);
758
759 if (node) {
760 info = node->data;
761
762 g_message ("Mount:'%s' with UUID:'%s' now unmounted from:'%s'",
763 name,
764 info->uuid,
765 mount_point);
766
767 g_signal_emit (storage, signals[MOUNT_POINT_REMOVED], 0, info->uuid, mount_point, NULL);
768
769 g_hash_table_remove (priv->mounts_by_uuid, info->uuid);
770 mount_node_free (node);
771 } else {
772 g_message ("Mount:'%s' now unmounted from:'%s' (was not tracked)",
773 name,
774 mount_point);
775 }
776
777 g_free (name);
778 g_free (mount_point);
779 g_object_unref (file);
780 }
781
782 static void
mount_removed_cb(GVolumeMonitor * monitor,GMount * mount,gpointer user_data)783 mount_removed_cb (GVolumeMonitor *monitor,
784 GMount *mount,
785 gpointer user_data)
786 {
787 TrackerStorage *storage;
788 TrackerStoragePrivate *priv;
789
790 storage = user_data;
791 priv = tracker_storage_get_instance_private (storage);
792
793 mount_remove (storage, mount);
794
795 /* Unmount suceeded, remove the pending check */
796 g_hash_table_remove (priv->unmount_watchdogs, mount);
797 }
798
799 static gboolean
unmount_failed_cb(gpointer user_data)800 unmount_failed_cb (gpointer user_data)
801 {
802 UnmountCheckData *data = user_data;
803 TrackerStoragePrivate *priv;
804
805 /* If this timeout gets to be executed, this is due
806 * to a pre-unmount signal with no corresponding
807 * unmount in a timely fashion, we assume this is
808 * due to an error, and add back the still mounted
809 * path.
810 */
811 priv = tracker_storage_get_instance_private (data->storage);
812
813 g_warning ("Unmount operation failed, adding back mount point...");
814
815 mount_add (data->storage, data->mount);
816
817 g_hash_table_remove (priv->unmount_watchdogs, data->mount);
818 return FALSE;
819 }
820
821 static void
mount_pre_removed_cb(GVolumeMonitor * monitor,GMount * mount,gpointer user_data)822 mount_pre_removed_cb (GVolumeMonitor *monitor,
823 GMount *mount,
824 gpointer user_data)
825 {
826 TrackerStorage *storage;
827 TrackerStoragePrivate *priv;
828 UnmountCheckData *data;
829 guint id;
830
831 storage = user_data;
832 priv = tracker_storage_get_instance_private (storage);
833
834 mount_remove (storage, mount);
835
836 /* Add check for failed unmounts */
837 data = g_new (UnmountCheckData, 1);
838 data->storage = storage;
839 data->mount = mount;
840
841 id = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT_IDLE + 10, 3,
842 unmount_failed_cb,
843 data, (GDestroyNotify) g_free);
844 g_hash_table_insert (priv->unmount_watchdogs, data->mount,
845 GUINT_TO_POINTER (id));
846 }
847
848 /**
849 * tracker_storage_new:
850 *
851 * Creates a new instance of #TrackerStorage.
852 *
853 * Returns: The newly created #TrackerStorage.
854 *
855 * Since: 0.8
856 **/
857 TrackerStorage *
tracker_storage_new(void)858 tracker_storage_new (void)
859 {
860 return g_object_new (TRACKER_TYPE_STORAGE, NULL);
861 }
862
863 static void
get_mount_point_by_uuid_foreach(gpointer key,gpointer value,gpointer user_data)864 get_mount_point_by_uuid_foreach (gpointer key,
865 gpointer value,
866 gpointer user_data)
867 {
868 GetRoots *gr;
869 GNode *node;
870 MountInfo *info;
871 TrackerStorageType mount_type;
872
873 gr = user_data;
874 node = value;
875 info = node->data;
876 mount_type = mount_info_get_type (info);
877
878 /* is mount of the type we're looking for? */
879 if ((gr->exact_match && mount_type == gr->type) ||
880 (!gr->exact_match && (mount_type & gr->type))) {
881 gchar *normalized_mount_point;
882 gint len;
883
884 normalized_mount_point = g_strdup (info->mount_point);
885 len = strlen (normalized_mount_point);
886
887 /* Don't include trailing slashes */
888 if (len > 2 && normalized_mount_point[len - 1] == G_DIR_SEPARATOR) {
889 normalized_mount_point[len - 1] = '\0';
890 }
891
892 gr->roots = g_slist_prepend (gr->roots, normalized_mount_point);
893 }
894 }
895
896 /**
897 * tracker_storage_get_device_roots:
898 * @storage: A #TrackerStorage
899 * @type: A #TrackerStorageType
900 * @exact_match: if all devices should exactly match the types
901 *
902 * Returns: (transfer full) (element-type utf8): a #GSList of strings
903 * containing the root directories for devices with @type based on
904 * @exact_match. Each element must be freed using g_free() and the
905 * list itself through g_slist_free().
906 *
907 * Since: 0.8
908 **/
909 GSList *
tracker_storage_get_device_roots(TrackerStorage * storage,TrackerStorageType type,gboolean exact_match)910 tracker_storage_get_device_roots (TrackerStorage *storage,
911 TrackerStorageType type,
912 gboolean exact_match)
913 {
914 TrackerStoragePrivate *priv;
915 GetRoots gr;
916
917 g_return_val_if_fail (TRACKER_IS_STORAGE (storage), NULL);
918
919 priv = tracker_storage_get_instance_private (storage);
920
921 gr.roots = NULL;
922 gr.type = type;
923 gr.exact_match = exact_match;
924
925 g_hash_table_foreach (priv->mounts_by_uuid,
926 get_mount_point_by_uuid_foreach,
927 &gr);
928
929 return gr.roots;
930 }
931
932 /**
933 * tracker_storage_get_device_uuids:
934 * @storage: A #TrackerStorage
935 * @type: A #TrackerStorageType
936 * @exact_match: if all devices should exactly match the types
937 *
938 * Returns: (transfer full) (element-type utf8): a #GSList of
939 * strings containing the UUID for devices with @type based
940 * on @exact_match. Each element must be freed using g_free()
941 * and the list itself through g_slist_free().
942 *
943 * Since: 0.8
944 **/
945 GSList *
tracker_storage_get_device_uuids(TrackerStorage * storage,TrackerStorageType type,gboolean exact_match)946 tracker_storage_get_device_uuids (TrackerStorage *storage,
947 TrackerStorageType type,
948 gboolean exact_match)
949 {
950 TrackerStoragePrivate *priv;
951 GHashTableIter iter;
952 gpointer key, value;
953 GSList *uuids;
954
955 g_return_val_if_fail (TRACKER_IS_STORAGE (storage), NULL);
956
957 priv = tracker_storage_get_instance_private (storage);
958
959 uuids = NULL;
960
961 g_hash_table_iter_init (&iter, priv->mounts_by_uuid);
962
963 while (g_hash_table_iter_next (&iter, &key, &value)) {
964 const gchar *uuid;
965 GNode *node;
966 MountInfo *info;
967 TrackerStorageType mount_type;
968
969 uuid = key;
970 node = value;
971 info = node->data;
972
973 mount_type = mount_info_get_type (info);
974
975 /* is mount of the type we're looking for? */
976 if ((exact_match && mount_type == type) ||
977 (!exact_match && (mount_type & type))) {
978 uuids = g_slist_prepend (uuids, g_strdup (uuid));
979 }
980 }
981
982 return uuids;
983 }
984
985 /**
986 * tracker_storage_get_mount_point_for_uuid:
987 * @storage: A #TrackerStorage
988 * @uuid: A string pointer to the UUID for the %GVolume.
989 *
990 * Returns: The mount point for @uuid, this should not be freed.
991 *
992 * Since: 0.8
993 **/
994 const gchar *
tracker_storage_get_mount_point_for_uuid(TrackerStorage * storage,const gchar * uuid)995 tracker_storage_get_mount_point_for_uuid (TrackerStorage *storage,
996 const gchar *uuid)
997 {
998 TrackerStoragePrivate *priv;
999 GNode *node;
1000 MountInfo *info;
1001
1002 g_return_val_if_fail (TRACKER_IS_STORAGE (storage), NULL);
1003 g_return_val_if_fail (uuid != NULL, NULL);
1004
1005 priv = tracker_storage_get_instance_private (storage);
1006
1007 node = g_hash_table_lookup (priv->mounts_by_uuid, uuid);
1008
1009 if (!node) {
1010 return NULL;
1011 }
1012
1013 info = node->data;
1014
1015 return info->mount_point;
1016 }
1017
1018 /**
1019 * tracker_storage_get_type_for_uuid:
1020 * @storage: A #TrackerStorage
1021 * @uuid: A string pointer to the UUID for the %GVolume.
1022 *
1023 * Returns: The type flags for @uuid.
1024 *
1025 * Since: 0.10
1026 **/
1027 TrackerStorageType
tracker_storage_get_type_for_uuid(TrackerStorage * storage,const gchar * uuid)1028 tracker_storage_get_type_for_uuid (TrackerStorage *storage,
1029 const gchar *uuid)
1030 {
1031 TrackerStoragePrivate *priv;
1032 GNode *node;
1033 TrackerStorageType type = 0;
1034
1035 g_return_val_if_fail (TRACKER_IS_STORAGE (storage), 0);
1036 g_return_val_if_fail (uuid != NULL, 0);
1037
1038 priv = tracker_storage_get_instance_private (storage);
1039
1040 node = g_hash_table_lookup (priv->mounts_by_uuid, uuid);
1041
1042 if (node) {
1043 MountInfo *info;
1044
1045 info = node->data;
1046
1047 if (info->removable) {
1048 type |= TRACKER_STORAGE_REMOVABLE;
1049 }
1050 if (info->optical) {
1051 type |= TRACKER_STORAGE_OPTICAL;
1052 }
1053 }
1054
1055 return type;
1056 }
1057
1058 /**
1059 * tracker_storage_get_uuid_for_file:
1060 * @storage: A #TrackerStorage
1061 * @file: a file
1062 *
1063 * Returns the UUID of the removable device for @file
1064 *
1065 * Returns: Returns the UUID of the removable device for @file, this
1066 * should not be freed.
1067 *
1068 * Since: 0.8
1069 **/
1070 const gchar *
tracker_storage_get_uuid_for_file(TrackerStorage * storage,GFile * file)1071 tracker_storage_get_uuid_for_file (TrackerStorage *storage,
1072 GFile *file)
1073 {
1074 TrackerStoragePrivate *priv;
1075 gchar *path;
1076 MountInfo *info;
1077
1078 g_return_val_if_fail (TRACKER_IS_STORAGE (storage), FALSE);
1079
1080 path = g_file_get_path (file);
1081
1082 if (!path) {
1083 return NULL;
1084 }
1085
1086 /* Normalize all paths to have a / at the end */
1087 if (!g_str_has_suffix (path, G_DIR_SEPARATOR_S)) {
1088 gchar *norm_path;
1089
1090 norm_path = g_strconcat (path, G_DIR_SEPARATOR_S, NULL);
1091 g_free (path);
1092 path = norm_path;
1093 }
1094
1095 priv = tracker_storage_get_instance_private (storage);
1096
1097 info = mount_info_find (priv->mounts, path);
1098
1099 if (!info) {
1100 g_free (path);
1101 return NULL;
1102 }
1103
1104 /* g_debug ("Mount for path '%s' is '%s' (UUID:'%s')", */
1105 /* path, info->mount_point, info->uuid); */
1106
1107 g_free (path);
1108
1109 return info->uuid;
1110 }
1111
1112