1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2008 David Zeuthen <zeuthen@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20 
21 #include "config.h"
22 #include <glib/gi18n-lib.h>
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/sysmacros.h>
32 #include <mntent.h>
33 
34 #include <glib.h>
35 #include <glib-object.h>
36 
37 #include "udiskslogging.h"
38 #include "udisksmountmonitor.h"
39 #include "udisksmount.h"
40 #include "udisksprivate.h"
41 #include "udisksdaemonutil.h"
42 
43 /* build a %Ns format string macro with N == PATH_MAX */
44 #define xstr(s) str(s)
45 #define str(s) #s
46 #define PATH_MAX_FMT "%" xstr(PATH_MAX) "s"
47 
48 /**
49  * SECTION:udisksmountmonitor
50  * @title: UDisksMountMonitor
51  * @short_description: Monitors mounted filesystems or in-use swap devices
52  *
53  * This type is used for monitoring mounted devices and swap devices
54  * in use. On Linux, this is done by inspecting and monitoring the
55  * <literal>/proc/self/mountinfo</literal> and
56  * <literal>/proc/swaps</literal> files.
57  */
58 
59 /**
60  * UDisksMountMonitor:
61  *
62  * The #UDisksMountMonitor structure contains only private data and
63  * should only be accessed using the provided API.
64  */
65 struct _UDisksMountMonitor
66 {
67   GObject parent_instance;
68 
69   GIOChannel *mounts_channel;
70   GSource *mounts_watch_source;
71 
72   GIOChannel *swaps_channel;
73   GSource *swaps_watch_source;
74 
75   GList *mounts;
76   GList *old_mounts;
77   GMutex mounts_mutex;
78 
79   gchar *mountinfo_checksum;
80   gchar *swaps_checksum;
81 
82   GMainContext *monitor_context;
83 };
84 
85 typedef struct _UDisksMountMonitorClass UDisksMountMonitorClass;
86 
87 struct _UDisksMountMonitorClass
88 {
89   GObjectClass parent_class;
90 
91   void (*mount_added)   (UDisksMountMonitor  *monitor,
92                          UDisksMount         *mount);
93   void (*mount_removed) (UDisksMountMonitor  *monitor,
94                          UDisksMount         *mount);
95 };
96 
97 /*--------------------------------------------------------------------------------------------------------------*/
98 
99 enum
100   {
101     MOUNT_ADDED_SIGNAL,
102     MOUNT_REMOVED_SIGNAL,
103     LAST_SIGNAL,
104   };
105 
106 static guint signals[LAST_SIGNAL] = { 0 };
107 
108 G_DEFINE_TYPE (UDisksMountMonitor, udisks_mount_monitor, G_TYPE_OBJECT)
109 
110 static void udisks_mount_monitor_ensure (UDisksMountMonitor *monitor);
111 static void udisks_mount_monitor_constructed (GObject *object);
112 
113 static void
udisks_mount_monitor_finalize(GObject * object)114 udisks_mount_monitor_finalize (GObject *object)
115 {
116   UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (object);
117 
118   if (monitor->mounts_channel != NULL)
119     g_io_channel_unref (monitor->mounts_channel);
120   if (monitor->mounts_watch_source != NULL)
121     g_source_destroy (monitor->mounts_watch_source);
122 
123   if (monitor->swaps_channel != NULL)
124     g_io_channel_unref (monitor->swaps_channel);
125   if (monitor->swaps_watch_source != NULL)
126     g_source_destroy (monitor->swaps_watch_source);
127 
128   if (monitor->monitor_context != NULL)
129     g_main_context_unref (monitor->monitor_context);
130 
131   g_list_free_full (monitor->mounts, g_object_unref);
132   g_list_free_full (monitor->old_mounts, g_object_unref);
133 
134   g_free (monitor->mountinfo_checksum);
135   g_free (monitor->swaps_checksum);
136 
137   g_mutex_clear (&monitor->mounts_mutex);
138 
139   if (G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->finalize != NULL)
140     G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->finalize (object);
141 }
142 
143 static void
udisks_mount_monitor_init(UDisksMountMonitor * monitor)144 udisks_mount_monitor_init (UDisksMountMonitor *monitor)
145 {
146   monitor->mounts = NULL;
147   monitor->old_mounts = NULL;
148   g_mutex_init (&monitor->mounts_mutex);
149 }
150 
151 static void
udisks_mount_monitor_class_init(UDisksMountMonitorClass * klass)152 udisks_mount_monitor_class_init (UDisksMountMonitorClass *klass)
153 {
154   GObjectClass *gobject_class = (GObjectClass *) klass;
155 
156   gobject_class->finalize    = udisks_mount_monitor_finalize;
157   gobject_class->constructed = udisks_mount_monitor_constructed;
158 
159   /**
160    * UDisksMountMonitor::mount-added
161    * @monitor: A #UDisksMountMonitor.
162    * @mount: The #UDisksMount that was added.
163    *
164    * Emitted when a mount is added.
165    *
166    * This signal is emitted in the
167    * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
168    * that @monitor was created in.
169    */
170   signals[MOUNT_ADDED_SIGNAL] = g_signal_new ("mount-added",
171                                               G_OBJECT_CLASS_TYPE (klass),
172                                               G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
173                                               G_STRUCT_OFFSET (UDisksMountMonitorClass, mount_added),
174                                               NULL,
175                                               NULL,
176                                               g_cclosure_marshal_VOID__OBJECT,
177                                               G_TYPE_NONE,
178                                               1,
179                                               UDISKS_TYPE_MOUNT);
180 
181   /**
182    * UDisksMountMonitor::mount-removed
183    * @monitor: A #UDisksMountMonitor.
184    * @mount: The #UDisksMount that was removed.
185    *
186    * Emitted when a mount is removed.
187    *
188    * This signal is emitted in the
189    * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
190    * that @monitor was created in.
191    */
192   signals[MOUNT_REMOVED_SIGNAL] = g_signal_new ("mount-removed",
193                                                 G_OBJECT_CLASS_TYPE (klass),
194                                                 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
195                                                 G_STRUCT_OFFSET (UDisksMountMonitorClass, mount_removed),
196                                                 NULL,
197                                                 NULL,
198                                                 g_cclosure_marshal_VOID__OBJECT,
199                                                 G_TYPE_NONE,
200                                                 1,
201                                                 UDISKS_TYPE_MOUNT);
202 }
203 
204 static void
diff_sorted_lists(GList * list1,GList * list2,GCompareFunc compare,GList ** added,GList ** removed)205 diff_sorted_lists (GList *list1,
206                    GList *list2,
207                    GCompareFunc compare,
208                    GList **added,
209                    GList **removed)
210 {
211   int order;
212 
213   *added = *removed = NULL;
214 
215   while (list1 != NULL && list2 != NULL)
216     {
217       order = (*compare) (list1->data, list2->data);
218       if (order < 0)
219         {
220           *removed = g_list_prepend (*removed, list1->data);
221           list1 = list1->next;
222         }
223       else if (order > 0)
224         {
225           *added = g_list_prepend (*added, list2->data);
226           list2 = list2->next;
227         }
228       else
229         { /* same item */
230           list1 = list1->next;
231           list2 = list2->next;
232         }
233     }
234 
235   while (list1 != NULL)
236     {
237       *removed = g_list_prepend (*removed, list1->data);
238       list1 = list1->next;
239     }
240   while (list2 != NULL)
241     {
242       *added = g_list_prepend (*added, list2->data);
243       list2 = list2->next;
244     }
245 }
246 
247 static void
reload_mounts(UDisksMountMonitor * monitor)248 reload_mounts (UDisksMountMonitor *monitor)
249 {
250   GList *cur_mounts;
251   GList *added;
252   GList *removed;
253   GList *l;
254 
255   udisks_mount_monitor_ensure (monitor);
256 
257   g_mutex_lock (&monitor->mounts_mutex);
258   cur_mounts = g_list_copy_deep (monitor->mounts, (GCopyFunc) udisks_g_object_ref_copy, NULL);
259   g_mutex_unlock (&monitor->mounts_mutex);
260 
261   /* no need to lock monitor->old_mounts as reload_mounts() should
262    * always be called from monitor->monitor_context. */
263   cur_mounts = g_list_sort (cur_mounts, (GCompareFunc) udisks_mount_compare);
264   diff_sorted_lists (monitor->old_mounts, cur_mounts, (GCompareFunc) udisks_mount_compare, &added, &removed);
265 
266   for (l = removed; l != NULL; l = l->next)
267     {
268       UDisksMount *mount = UDISKS_MOUNT (l->data);
269       g_signal_emit (monitor, signals[MOUNT_REMOVED_SIGNAL], 0, mount);
270     }
271 
272   for (l = added; l != NULL; l = l->next)
273     {
274       UDisksMount *mount = UDISKS_MOUNT (l->data);
275       g_signal_emit (monitor, signals[MOUNT_ADDED_SIGNAL], 0, mount);
276     }
277 
278   g_list_free_full (monitor->old_mounts, g_object_unref);
279   monitor->old_mounts = cur_mounts;
280 
281   g_list_free (removed);
282   g_list_free (added);
283 }
284 
285 static gboolean
mounts_changed_event(GIOChannel * channel,GIOCondition cond,gpointer user_data)286 mounts_changed_event (GIOChannel *channel,
287                       GIOCondition cond,
288                       gpointer user_data)
289 {
290   UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (user_data);
291   if (cond & ~G_IO_ERR)
292     goto out;
293   reload_mounts (monitor);
294  out:
295   return TRUE;
296 }
297 
298 static gboolean
swaps_changed_event(GIOChannel * channel,GIOCondition cond,gpointer user_data)299 swaps_changed_event (GIOChannel *channel,
300                      GIOCondition cond,
301                      gpointer user_data)
302 {
303   UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (user_data);
304   if (cond & ~G_IO_ERR)
305     goto out;
306   reload_mounts (monitor);
307  out:
308   return TRUE;
309 }
310 
311 static gboolean
mounts_changed_idle_cb(gpointer user_data)312 mounts_changed_idle_cb (gpointer user_data)
313 {
314   UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (user_data);
315 
316   reload_mounts (monitor);
317 
318   /* remove the source */
319   return FALSE;
320 }
321 
322 static void
udisks_mount_monitor_constructed(GObject * object)323 udisks_mount_monitor_constructed (GObject *object)
324 {
325   UDisksMountMonitor *monitor = UDISKS_MOUNT_MONITOR (object);
326   GError *error;
327 
328   monitor->monitor_context = g_main_context_ref_thread_default ();
329 
330   /* fetch initial data */
331   udisks_mount_monitor_ensure (monitor);
332 
333   error = NULL;
334   monitor->mounts_channel = g_io_channel_new_file ("/proc/self/mountinfo", "r", &error);
335   if (monitor->mounts_channel != NULL)
336     {
337       monitor->mounts_watch_source = g_io_create_watch (monitor->mounts_channel, G_IO_ERR);
338 #if __GNUC__ >= 8
339 #pragma GCC diagnostic push
340 #pragma GCC diagnostic ignored "-Wcast-function-type"
341 #endif
342 /* parameters of the callback depend on the source and can be different
343  * from the required "generic" GSourceFunc, see:
344  * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-callback
345  */
346       g_source_set_callback (monitor->mounts_watch_source, (GSourceFunc) mounts_changed_event, monitor, NULL);
347 #if __GNUC__ >= 8
348 #pragma GCC diagnostic pop
349 #endif
350       g_source_attach (monitor->mounts_watch_source, monitor->monitor_context);
351       g_source_unref (monitor->mounts_watch_source);
352     }
353   else
354     {
355       g_error ("No /proc/self/mountinfo file: %s", error->message);
356       g_clear_error (&error);
357     }
358 
359   error = NULL;
360   monitor->swaps_channel = g_io_channel_new_file ("/proc/swaps", "r", &error);
361   if (monitor->swaps_channel != NULL)
362     {
363       monitor->swaps_watch_source = g_io_create_watch (monitor->swaps_channel, G_IO_ERR);
364 #if __GNUC__ >= 8
365 #pragma GCC diagnostic push
366 #pragma GCC diagnostic ignored "-Wcast-function-type"
367 #endif
368 /* parameters of the callback depend on the source and can be different
369  * from the required "generic" GSourceFunc, see:
370  * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-source-set-callback
371  */
372       g_source_set_callback (monitor->swaps_watch_source, (GSourceFunc) swaps_changed_event, monitor, NULL);
373 #if __GNUC__ >= 8
374 #pragma GCC diagnostic pop
375 #endif
376       g_source_attach (monitor->swaps_watch_source, monitor->monitor_context);
377       g_source_unref (monitor->swaps_watch_source);
378     }
379   else
380     {
381       if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT))
382         {
383           udisks_warning ("Error opening /proc/swaps file: %s (%s, %d)",
384                           error->message, g_quark_to_string (error->domain), error->code);
385         }
386       g_clear_error (&error);
387     }
388 
389   if (G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->constructed != NULL)
390     (*G_OBJECT_CLASS (udisks_mount_monitor_parent_class)->constructed) (object);
391 }
392 
393 /**
394  * udisks_mount_monitor_new:
395  *
396  * Creates a new #UDisksMountMonitor object.
397  *
398  * Signals are emitted in the <link
399  * linkend="g-main-context-push-thread-default">thread-default main
400  * loop</link> that this function is called from.
401  *
402  * Returns: A #UDisksMountMonitor. Free with g_object_unref().
403  */
404 UDisksMountMonitor *
udisks_mount_monitor_new(void)405 udisks_mount_monitor_new (void)
406 {
407   return UDISKS_MOUNT_MONITOR (g_object_new (UDISKS_TYPE_MOUNT_MONITOR, NULL));
408 }
409 
410 static gboolean
have_mount(UDisksMountMonitor * monitor,dev_t dev,const gchar * mount_point)411 have_mount (UDisksMountMonitor *monitor,
412             dev_t               dev,
413             const gchar        *mount_point)
414 {
415   GList *l;
416   gboolean ret;
417 
418   ret = FALSE;
419 
420   for (l = monitor->mounts; l != NULL; l = l->next)
421     {
422       UDisksMount *mount = UDISKS_MOUNT (l->data);
423       if (udisks_mount_get_dev (mount) == dev &&
424           g_strcmp0 (udisks_mount_get_mount_path (mount), mount_point) == 0)
425         {
426           ret = TRUE;
427           break;
428         }
429     }
430 
431   return ret;
432 }
433 
434 /* ---------------------------------------------------------------------------------------------------- */
435 
436 static gboolean
udisks_mount_monitor_read_mountinfo(gchar ** contents,gsize * length)437 udisks_mount_monitor_read_mountinfo (gchar  **contents,
438                                      gsize   *length)
439 {
440   GError *error = NULL;
441 
442   if (!g_file_get_contents ("/proc/self/mountinfo", contents, length, &error))
443     {
444       udisks_warning ("Error reading /proc/self/mountinfo: %s (%s, %d)",
445                       error->message, g_quark_to_string (error->domain), error->code);
446       g_clear_error (&error);
447       return FALSE;
448     }
449 
450   return TRUE;
451 }
452 
453 static void
udisks_mount_monitor_parse_mountinfo(UDisksMountMonitor * monitor,const gchar * contents)454 udisks_mount_monitor_parse_mountinfo (UDisksMountMonitor  *monitor,
455                                       const gchar         *contents)
456 {
457   gchar **lines;
458   guint n;
459 
460   /* See Documentation/filesystems/proc.txt for the format of /proc/self/mountinfo
461    *
462    * Note that things like space are encoded as \020.
463    */
464   if (contents == NULL)
465     return;
466 
467   lines = g_strsplit (contents, "\n", 0);
468   for (n = 0; lines[n] != NULL; n++)
469     {
470       guint mount_id;
471       guint parent_id;
472       guint major, minor;
473       gchar encoded_root[PATH_MAX + 1];
474       gchar encoded_mount_point[PATH_MAX + 1];
475       gchar *mount_point;
476       dev_t dev;
477 
478       if (strlen (lines[n]) == 0)
479         continue;
480 
481       if (sscanf (lines[n],
482                   "%u %u %u:%u " PATH_MAX_FMT " " PATH_MAX_FMT,
483                   &mount_id,
484                   &parent_id,
485                   &major,
486                   &minor,
487                   encoded_root,
488                   encoded_mount_point) != 6)
489         {
490           udisks_warning ("Error parsing line '%s'", lines[n]);
491           continue;
492         }
493       encoded_root[sizeof encoded_root - 1] = '\0';
494       encoded_mount_point[sizeof encoded_mount_point - 1] = '\0';
495 
496       /* Temporary work-around for btrfs, see
497        *
498        *  https://bugzilla.redhat.com/show_bug.cgi?id=495152#c31
499        *  http://article.gmane.org/gmane.comp.file-systems.btrfs/2851
500        *
501        * for details.
502        */
503       if (major == 0)
504         {
505           const gchar *sep;
506           sep = strstr (lines[n], " - ");
507           if (sep != NULL)
508             {
509               gchar fstype[PATH_MAX + 1];
510               gchar mount_source[PATH_MAX + 1];
511               struct stat statbuf;
512 
513               if (sscanf (sep + 3, PATH_MAX_FMT " " PATH_MAX_FMT, fstype, mount_source) != 2)
514                 {
515                   udisks_warning ("Error parsing things past - for '%s'", lines[n]);
516                   continue;
517                 }
518               fstype[sizeof fstype - 1] = '\0';
519               mount_source[sizeof mount_source - 1] = '\0';
520 
521               if (g_strcmp0 (fstype, "btrfs") != 0)
522                 continue;
523 
524               if (!g_str_has_prefix (mount_source, "/dev/"))
525                 continue;
526 
527               if (stat (mount_source, &statbuf) != 0)
528                 {
529                   udisks_warning ("Error statting %s: %m", mount_source);
530                   continue;
531                 }
532 
533               if (!S_ISBLK (statbuf.st_mode))
534                 {
535                   udisks_warning ("%s is not a block device", mount_source);
536                   continue;
537                 }
538 
539               dev = statbuf.st_rdev;
540             }
541           else
542             {
543               continue;
544             }
545         }
546       else
547         {
548           dev = makedev (major, minor);
549         }
550 
551       mount_point = g_strcompress (encoded_mount_point);
552 
553       /* TODO: we can probably use a hash table or something if this turns out to be slow */
554       if (!have_mount (monitor, dev, mount_point))
555         {
556           UDisksMount *mount;
557           mount = _udisks_mount_new (dev, mount_point, UDISKS_MOUNT_TYPE_FILESYSTEM);
558           monitor->mounts = g_list_prepend (monitor->mounts, mount);
559         }
560 
561       g_free (mount_point);
562     }
563   g_strfreev (lines);
564 }
565 
566 /* ---------------------------------------------------------------------------------------------------- */
567 
568 static gboolean
udisks_mount_monitor_read_swaps(gchar ** contents,gsize * length)569 udisks_mount_monitor_read_swaps (gchar  **contents,
570                                  gsize   *length)
571 {
572   GError *error = NULL;
573 
574   if (!g_file_get_contents ("/proc/swaps", contents, length, &error))
575     {
576       if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
577         {
578           g_clear_error (&error);
579           return TRUE;
580         }
581       else
582         {
583           udisks_warning ("Error reading /proc/swaps: %s (%s, %d)",
584                           error->message, g_quark_to_string (error->domain), error->code);
585           g_clear_error (&error);
586           return FALSE;
587         }
588     }
589 
590   return TRUE;
591 }
592 
593 static void
udisks_mount_monitor_parse_swaps(UDisksMountMonitor * monitor,const gchar * contents)594 udisks_mount_monitor_parse_swaps (UDisksMountMonitor  *monitor,
595                                   const gchar         *contents)
596 {
597   gchar **lines;
598   guint n;
599 
600   if (contents == NULL)
601     return;
602 
603   lines = g_strsplit (contents, "\n", 0);
604   for (n = 0; lines[n] != NULL; n++)
605     {
606       gchar filename[PATH_MAX + 1];
607       struct stat statbuf;
608       dev_t dev;
609 
610       /* skip first line of explanatory text */
611       if (n == 0)
612         continue;
613 
614       if (strlen (lines[n]) == 0)
615         continue;
616 
617       if (sscanf (lines[n], PATH_MAX_FMT, filename) != 1)
618         {
619           udisks_warning ("Error parsing line '%s'", lines[n]);
620           continue;
621         }
622       filename[sizeof filename - 1] = '\0';
623 
624       if (stat (filename, &statbuf) != 0)
625         {
626           udisks_warning ("Error statting %s: %m", filename);
627           continue;
628         }
629 
630       dev = statbuf.st_rdev;
631 
632       if (!have_mount (monitor, dev, NULL))
633         {
634           UDisksMount *mount;
635           mount = _udisks_mount_new (dev, NULL, UDISKS_MOUNT_TYPE_SWAP);
636           monitor->mounts = g_list_prepend (monitor->mounts, mount);
637         }
638     }
639   g_strfreev (lines);
640 }
641 
642 /* ---------------------------------------------------------------------------------------------------- */
643 
644 static void
udisks_mount_monitor_ensure(UDisksMountMonitor * monitor)645 udisks_mount_monitor_ensure (UDisksMountMonitor *monitor)
646 {
647   gchar *mountinfo_contents = NULL;
648   gchar *swaps_contents = NULL;
649   gsize mountinfo_length = 0;
650   gsize swaps_length = 0;
651   gchar *mountinfo_checksum = NULL;
652   gchar *swaps_checksum = NULL;
653   GSource *idle_source;
654   gboolean have_mountinfo;
655   gboolean have_swaps;
656 
657   g_mutex_lock (&monitor->mounts_mutex);
658 
659   have_mountinfo = udisks_mount_monitor_read_mountinfo (&mountinfo_contents, &mountinfo_length);
660   have_swaps = udisks_mount_monitor_read_swaps (&swaps_contents, &swaps_length);
661   if (have_mountinfo || have_swaps)
662     {
663       /* compute contents checksums and compare them against current cache */
664       if (mountinfo_contents)
665         mountinfo_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
666                                                           (const guchar *) mountinfo_contents,
667                                                           mountinfo_length);
668       if (swaps_contents)
669         swaps_checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1,
670                                                       (const guchar *) swaps_contents,
671                                                       swaps_length);
672       if (g_strcmp0 (mountinfo_checksum, monitor->mountinfo_checksum) != 0 ||
673           g_strcmp0 (swaps_checksum, monitor->swaps_checksum) != 0)
674         {
675           g_list_free_full (monitor->mounts, g_object_unref);
676           monitor->mounts = NULL;
677 
678           udisks_mount_monitor_parse_mountinfo (monitor, mountinfo_contents);
679           udisks_mount_monitor_parse_swaps (monitor, swaps_contents);
680 
681           /* save current checksums */
682           g_free (monitor->mountinfo_checksum);
683           g_free (monitor->swaps_checksum);
684           monitor->mountinfo_checksum = g_strdup (mountinfo_checksum);
685           monitor->swaps_checksum = g_strdup (swaps_checksum);
686 
687           /* notify about the changes */
688           idle_source = g_idle_source_new ();
689           g_source_set_priority (idle_source, G_PRIORITY_DEFAULT_IDLE);
690           g_source_set_callback (idle_source, (GSourceFunc) mounts_changed_idle_cb, monitor, NULL);
691           g_source_attach (idle_source, monitor->monitor_context);
692           g_source_unref (idle_source);
693         }
694 
695         g_free (mountinfo_checksum);
696         g_free (swaps_checksum);
697     }
698   g_free (mountinfo_contents);
699   g_free (swaps_contents);
700 
701   g_mutex_unlock (&monitor->mounts_mutex);
702 }
703 
704 /**
705  * udisks_mount_monitor_get_mounts_for_dev:
706  * @monitor: A #UDisksMountMonitor.
707  * @dev: A #dev_t device number.
708  *
709  * Gets all #UDisksMount objects for @dev.
710  *
711  * Returns: A #GList of #UDisksMount objects. The returned list must
712  * be freed with g_list_free() after each element has been freed with
713  * g_object_unref().
714  */
715 GList *
udisks_mount_monitor_get_mounts_for_dev(UDisksMountMonitor * monitor,dev_t dev)716 udisks_mount_monitor_get_mounts_for_dev (UDisksMountMonitor *monitor,
717                                          dev_t               dev)
718 {
719   GList *ret;
720   GList *l;
721 
722   ret = NULL;
723 
724   udisks_mount_monitor_ensure (monitor);
725 
726   g_mutex_lock (&monitor->mounts_mutex);
727 
728   for (l = monitor->mounts; l != NULL; l = l->next)
729     {
730       UDisksMount *mount = UDISKS_MOUNT (l->data);
731 
732       if (udisks_mount_get_dev (mount) == dev)
733         {
734           ret = g_list_prepend (ret, g_object_ref (mount));
735         }
736     }
737 
738   g_mutex_unlock (&monitor->mounts_mutex);
739 
740   /* Sort the list to ensure that shortest mount paths appear first */
741   ret = g_list_sort (ret, (GCompareFunc) udisks_mount_compare);
742 
743   return ret;
744 }
745 
746 /**
747  * udisks_mount_monitor_is_dev_in_use:
748  * @monitor: A #UDisksMountMonitor.
749  * @dev: A #dev_t device number.
750  * @out_type: (out allow-none): Return location for mount type, if in use or %NULL.
751  *
752  * Checks if @dev is in use (e.g. mounted or swap-area in-use).
753  *
754  * Returns: %TRUE if in use, %FALSE otherwise.
755  */
756 gboolean
udisks_mount_monitor_is_dev_in_use(UDisksMountMonitor * monitor,dev_t dev,UDisksMountType * out_type)757 udisks_mount_monitor_is_dev_in_use (UDisksMountMonitor  *monitor,
758                                     dev_t                dev,
759                                     UDisksMountType     *out_type)
760 {
761   gboolean ret;
762   GList *l;
763 
764   ret = FALSE;
765   udisks_mount_monitor_ensure (monitor);
766 
767   g_mutex_lock (&monitor->mounts_mutex);
768 
769   for (l = monitor->mounts; l != NULL; l = l->next)
770     {
771       UDisksMount *mount = UDISKS_MOUNT (l->data);
772 
773       if (udisks_mount_get_dev (mount) == dev)
774         {
775           if (out_type != NULL)
776             *out_type = udisks_mount_get_mount_type (mount);
777           ret = TRUE;
778           goto out;
779         }
780     }
781 
782  out:
783   g_mutex_unlock (&monitor->mounts_mutex);
784   return ret;
785 }
786 
787 /**
788  * udisks_mount_monitor_get_mount_for_path:
789  * @monitor: A #UDisksMountMonitor.
790  * @mount_path: A filesystem path on which a device may be mounted.
791  *
792  * Gets the #UDisksMount mounted at @mount_path, if any.
793  *
794  * Returns: (transfer full) (nullable): the #UDisksMount (of type
795  *  #UDISKS_MOUNT_TYPE_FILESYSTEM) mounted at @mount_path, or %NULL if nothing
796  *  is mounted there.
797  */
798 UDisksMount *
udisks_mount_monitor_get_mount_for_path(UDisksMountMonitor * monitor,const gchar * mount_path)799 udisks_mount_monitor_get_mount_for_path (UDisksMountMonitor  *monitor,
800                                          const gchar         *mount_path)
801 {
802   GList *l;
803 
804   g_return_val_if_fail (UDISKS_IS_MOUNT_MONITOR (monitor), NULL);
805   g_return_val_if_fail (mount_path != NULL, NULL);
806 
807   udisks_mount_monitor_ensure (monitor);
808 
809   g_mutex_lock (&monitor->mounts_mutex);
810 
811   for (l = monitor->mounts; l != NULL; l = l->next)
812     {
813       UDisksMount *mount = UDISKS_MOUNT (l->data);
814 
815       if (udisks_mount_get_mount_type (mount) == UDISKS_MOUNT_TYPE_FILESYSTEM &&
816           g_strcmp0 (udisks_mount_get_mount_path (mount), mount_path) == 0)
817         {
818           g_object_ref (mount);
819           g_mutex_unlock (&monitor->mounts_mutex);
820           return mount;
821         }
822     }
823 
824   g_mutex_unlock (&monitor->mounts_mutex);
825   return NULL;
826 }
827