1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2011 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 
23 #include <glib/gi18n-lib.h>
24 
25 #include <glib/gstdio.h>
26 
27 #include <sys/stat.h>
28 #include <sys/sysmacros.h>
29 #include <fcntl.h>
30 #include <sys/ioctl.h>
31 #include <linux/loop.h>
32 
33 #include <limits.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 
37 #include "udisksdaemon.h"
38 #include "udisksstate.h"
39 #include "udisksmount.h"
40 #include "udisksmountmonitor.h"
41 #include "udiskslogging.h"
42 #include "udiskslinuxprovider.h"
43 #include "udisksdaemonutil.h"
44 #include "udiskslinuxencryptedhelpers.h"
45 #include "udiskslinuxblockobject.h"
46 
47 /**
48  * SECTION:udisksstate
49  * @title: UDisksState
50  * @short_description: Object used for recording state and cleaning up
51  *
52  * This type is used for recording actions done by users and cleaning
53  * up when devices set up via the udisks interfaces are removed while
54  * still in use - for example, a USB stick being yanked.
55  *
56  * The following files are used:
57  * <table frame='all'>
58  *   <title>Persistent information and state</title>
59  *   <tgroup cols='2' align='left' colsep='1' rowsep='1'>
60  *     <thead>
61  *       <row>
62  *         <entry>File</entry>
63  *         <entry>Usage</entry>
64  *       </row>
65  *     </thead>
66  *     <tbody>
67  *       <row>
68  *         <entry><filename>/run/udisks2/mounted-fs</filename></entry>
69  *         <entry>
70  *           A serialized 'a{sa{sv}}' #GVariant mapping from the
71  *           mount point (e.g. <filename>/media/EOS_DIGITAL</filename>) into a set of details.
72  *           Known details include
73  *           <literal>block-device</literal>
74  *           (of type <link linkend="G-VARIANT-TYPE-UINT64:CAPS">'t'</link>) that is the #dev_t
75  *           for the mounted device,
76  *           <literal>mounted-by-uid</literal>
77  *           (of type <link linkend="G-VARIANT-TYPE-UINT32:CAPS">'u'</link>) that is the #uid_t
78  *           of the user who mounted the device, and
79  *           <literal>fstab-mount</literal>
80  *           (of type <link linkend="G-VARIANT-TYPE-BOOLEAN:CAPS">'b'</link>) that is %TRUE
81  *           if the device was mounted via an entry in /etc/fstab.
82  *
83  *           Contains only non-persistent mount points. This state file is typically stored on
84  *           a non-persistent filesystem.
85  *         </entry>
86  *       </row>
87  *       <row>
88  *         <entry><filename>/var/lib/udisks2/mounted-fs-persistent</filename></entry>
89  *         <entry>
90  *           Similar in contents and data structure to <filename>/run/udisks2/mounted-fs</filename>
91  *           except that only persistent mount points are stored there. This state file is
92  *           typically stored on a persistent filesystem.
93  *         </entry>
94  *       </row>
95  *       <row>
96  *         <entry><filename>/run/udisks2/unlocked-crypto-dev</filename></entry>
97  *         <entry>
98  *           A serialized 'a{ta{sv}}' #GVariant mapping from the
99  *           #dev_t of the clear-text device (e.g. <filename>/dev/dm-0</filename>) into a set of details.
100  *           Known details include
101  *           <literal>crypto-device</literal>
102  *           (of type <link linkend="G-VARIANT-TYPE-UINT64:CAPS">'t'</link>) that is the #dev_t
103  *           for the crypto-text device,
104  *           <literal>dm-uuid</literal>
105  *           (of type <link linkend="G-VARIANT-TYPE-ARRAY:CAPS">'ay'</link>) that is the device mapper UUID
106  *           for the clear-text device and
107  *           <literal>unlocked-by-uid</literal>
108  *           (of type <link linkend="G-VARIANT-TYPE-UINT32:CAPS">'u'</link>) that is the #uid_t
109  *           of the user who unlocked the device.
110  *         </entry>
111  *       </row>
112  *       <row>
113  *         <entry><filename>/run/udisks2/loop</filename></entry>
114  *         <entry>
115  *           A serialized 'a{sa{sv}}' #GVariant mapping from the
116  *           loop device name (e.g. <filename>/dev/loop0</filename>) into a set of details.
117  *           Known details include
118  *           <literal>backing-file</literal>
119  *           (of type <link linkend="G-VARIANT-TYPE-ARRAY:CAPS">'ay'</link>) for the name of the backing file and
120  *           <literal>backing-file-device</literal>
121  *           (of type <link linkend="G-VARIANT-TYPE-UINT64:CAPS">'t'</link>) for the #dev_t
122  *           for of the device holding the backing file (or 0 if unknown) and
123  *           <literal>setup-by-uid</literal>
124  *           (of type <link linkend="G-VARIANT-TYPE-UINT32:CAPS">'u'</link>) that is the #uid_t
125  *           of the user who set up the loop device.
126  *         </entry>
127  *       </row>
128  *       <row>
129  *         <entry><filename>/run/udisks2/mdraid</filename></entry>
130  *         <entry>
131  *           A serialized 'a{ta{sv}}' #GVariant mapping from the
132  *           #dev_t of the raid device (e.g. <filename>/dev/md127</filename>) into a set of details.
133  *           Known details include
134  *           <literal>started-by-uid</literal>
135  *           (of type <link linkend="G-VARIANT-TYPE-UINT32:CAPS">'u'</link>) that is the #uid_t
136  *           of the user who started the array.
137  *         </entry>
138  *       </row>
139  *       <row>
140  *         <entry><filename>/run/udisks2/modules</filename></entry>
141  *         <entry>
142  *           A serialized 'a{sa{sv}}' #GVariant mapping from the
143  *           module name (e.g. <filename>lvm2</filename>) into a set of details.
144  *           No details are defined at this point, reserved for future use.
145  *
146  *           Contains currently active module names primarily for the purpose
147  *           of crash recovery upon next daemon start.
148  *         </entry>
149  *       </row>
150  *     </tbody>
151  *   </tgroup>
152  * </table>
153  * Cleaning up is implemented by running a thread (to ensure that
154  * actions are serialized) that checks all data in the files mentioned
155  * above and cleans up the entry in question by e.g. unmounting a
156  * filesystem, removing a mount point or tearing down a device-mapper
157  * device when needed. The clean-up thread itself needs to be manually
158  * kicked using e.g. udisks_state_check() from suitable places in
159  * the #UDisksDaemon and #UDisksProvider implementations.
160  *
161  * Since cleaning up is only necessary when a device has been removed
162  * without having been properly stopped or shut down, the fact that it
163  * was cleaned up is logged to ensure that the information is brought
164  * to the attention of the system administrator.
165  */
166 
167 /* State file filenames */
168 #define UDISKS_STATE_FILE_MOUNTED_FS             "mounted-fs"
169 #define UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT  "mounted-fs-persistent"
170 #define UDISKS_STATE_FILE_UNLOCKED_CRYPTO_DEV    "unlocked-crypto-dev"
171 #define UDISKS_STATE_FILE_LOOP                   "loop"
172 #define UDISKS_STATE_FILE_MDRAID                 "mdraid"
173 #define UDISKS_STATE_FILE_MODULES                "modules"
174 
175 /**
176  * UDisksState:
177  *
178  * The #UDisksState structure contains only private data and should
179  * only be accessed using the provided API.
180  */
181 struct _UDisksState
182 {
183   GObject parent_instance;
184 
185   GMutex lock;
186 
187   UDisksDaemon *daemon;
188 
189   GThread *thread;
190   GMainContext *context;
191   GMainLoop *loop;
192 
193   /* key-path -> GVariant */
194   GHashTable *cache;
195 };
196 
197 typedef struct _UDisksStateClass UDisksStateClass;
198 
199 struct _UDisksStateClass
200 {
201   GObjectClass parent_class;
202 };
203 
204 enum
205 {
206   PROP_0,
207   PROP_DAEMON
208 };
209 
210 static void      udisks_state_check_in_thread     (UDisksState          *state);
211 static void      udisks_state_check_mounted_fs    (UDisksState          *state,
212                                                    const gchar          *key,
213                                                    GArray               *devs_to_clean,
214                                                    dev_t                 match_block_device);
215 static void      udisks_state_check_unlocked_crypto_dev (UDisksState          *state,
216                                                          gboolean              check_only,
217                                                          GArray               *devs_to_clean);
218 static void      udisks_state_check_loop          (UDisksState          *state,
219                                                    gboolean              check_only,
220                                                    GArray               *devs_to_clean);
221 static void      udisks_state_check_mdraid        (UDisksState          *state,
222                                                    gboolean              check_only,
223                                                    GArray               *devs_to_clean);
224 static gchar    *get_state_file_path              (const gchar          *key);
225 static GVariant *udisks_state_get                 (UDisksState          *state,
226                                                    const gchar          *key,
227                                                    const GVariantType   *type);
228 static gboolean  udisks_state_set                 (UDisksState          *state,
229                                                    const gchar          *key,
230                                                    const GVariantType   *type,
231                                                    GVariant             *value);
232 
233 G_DEFINE_TYPE (UDisksState, udisks_state, G_TYPE_OBJECT);
234 
235 static void
udisks_state_init(UDisksState * state)236 udisks_state_init (UDisksState *state)
237 {
238   g_mutex_init (&state->lock);
239   state->cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
240 }
241 
242 static void
udisks_state_finalize(GObject * object)243 udisks_state_finalize (GObject *object)
244 {
245   UDisksState *state = UDISKS_STATE (object);
246 
247   g_hash_table_unref (state->cache);
248   g_mutex_clear (&state->lock);
249 
250   G_OBJECT_CLASS (udisks_state_parent_class)->finalize (object);
251 }
252 
253 static void
udisks_state_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)254 udisks_state_get_property (GObject    *object,
255                            guint       prop_id,
256                            GValue     *value,
257                            GParamSpec *pspec)
258 {
259   UDisksState *state = UDISKS_STATE (object);
260 
261   switch (prop_id)
262     {
263     case PROP_DAEMON:
264       g_value_set_object (value, udisks_state_get_daemon (state));
265       break;
266 
267     default:
268       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269       break;
270     }
271 }
272 
273 static void
udisks_state_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)274 udisks_state_set_property (GObject      *object,
275                            guint         prop_id,
276                            const GValue *value,
277                            GParamSpec   *pspec)
278 {
279   UDisksState *state = UDISKS_STATE (object);
280 
281   switch (prop_id)
282     {
283     case PROP_DAEMON:
284       g_assert (state->daemon == NULL);
285       /* we don't take a reference to the daemon */
286       state->daemon = g_value_get_object (value);
287       g_assert (state->daemon != NULL);
288       break;
289 
290     default:
291       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292       break;
293     }
294 }
295 
296 static void
udisks_state_class_init(UDisksStateClass * klass)297 udisks_state_class_init (UDisksStateClass *klass)
298 {
299   GObjectClass *gobject_class;
300 
301   gobject_class = G_OBJECT_CLASS (klass);
302   gobject_class->finalize = udisks_state_finalize;
303   gobject_class->set_property = udisks_state_set_property;
304   gobject_class->get_property = udisks_state_get_property;
305 
306   /**
307    * UDisksState:daemon:
308    *
309    * The #UDisksDaemon object.
310    */
311   g_object_class_install_property (gobject_class,
312                                    PROP_DAEMON,
313                                    g_param_spec_object ("daemon",
314                                                         "Daemon",
315                                                         "The daemon object",
316                                                         UDISKS_TYPE_DAEMON,
317                                                         G_PARAM_READABLE |
318                                                         G_PARAM_WRITABLE |
319                                                         G_PARAM_CONSTRUCT_ONLY |
320                                                         G_PARAM_STATIC_STRINGS));
321 }
322 
323 /**
324  * udisks_state_new:
325  * @daemon: A #UDisksDaemon.
326  *
327  * Creates a new #UDisksState object.
328  *
329  * Returns: A #UDisksState that should be freed with g_object_unref().
330  */
331 UDisksState *
udisks_state_new(UDisksDaemon * daemon)332 udisks_state_new (UDisksDaemon *daemon)
333 {
334   return UDISKS_STATE (g_object_new (UDISKS_TYPE_STATE,
335                                      "daemon", daemon,
336                                      NULL));
337 }
338 
339 static gpointer
udisks_state_thread_func(gpointer user_data)340 udisks_state_thread_func (gpointer user_data)
341 {
342   UDisksState *state = UDISKS_STATE (user_data);
343 
344   udisks_info ("Entering cleanup thread");
345 
346   g_main_loop_run (state->loop);
347 
348   state->thread = NULL;
349   g_main_loop_unref (state->loop);
350   state->loop = NULL;
351   g_main_context_unref (state->context);
352   state->context = NULL;
353   g_object_unref (state);
354 
355   udisks_info ("Exiting cleanup thread");
356   return NULL;
357 }
358 
359 
360 /**
361  * udisks_state_start_cleanup:
362  * @state: A #UDisksState.
363  *
364  * Starts the clean-up thread.
365  *
366  * The clean-up thread will hold a reference to @state for as long as
367  * it's running - use udisks_state_stop_cleanup() to stop it.
368  */
369 void
udisks_state_start_cleanup(UDisksState * state)370 udisks_state_start_cleanup (UDisksState *state)
371 {
372   g_return_if_fail (UDISKS_IS_STATE (state));
373   g_return_if_fail (state->thread == NULL);
374 
375   state->context = g_main_context_new ();
376   state->loop = g_main_loop_new (state->context, FALSE);
377   state->thread = g_thread_new ("cleanup",
378                                 udisks_state_thread_func,
379                                 g_object_ref (state));
380 }
381 
382 /**
383  * udisks_state_stop_cleanup:
384  * @state: A #UDisksState.
385  *
386  * Stops the clean-up thread. Blocks the calling thread until it has stopped.
387  */
388 void
udisks_state_stop_cleanup(UDisksState * state)389 udisks_state_stop_cleanup (UDisksState *state)
390 {
391   GThread *thread;
392 
393   g_return_if_fail (UDISKS_IS_STATE (state));
394   g_return_if_fail (state->thread != NULL);
395 
396   thread = state->thread;
397   g_main_loop_quit (state->loop);
398   g_thread_join (thread);
399 }
400 
401 static gboolean
udisks_state_check_func(gpointer user_data)402 udisks_state_check_func (gpointer user_data)
403 {
404   UDisksState *state = UDISKS_STATE (user_data);
405   udisks_state_check_in_thread (state);
406   return FALSE;
407 }
408 
409 /**
410  * udisks_state_check:
411  * @state: A #UDisksState.
412  *
413  * Causes the clean-up thread for @state to check if anything should be cleaned up.
414  *
415  * This can be called from any thread and will not block the calling thread.
416  */
417 void
udisks_state_check(UDisksState * state)418 udisks_state_check (UDisksState *state)
419 {
420   g_return_if_fail (UDISKS_IS_STATE (state));
421   g_return_if_fail (state->thread != NULL);
422 
423   g_main_context_invoke (state->context,
424                          udisks_state_check_func,
425                          state);
426 }
427 
428 
429 typedef struct
430 {
431   UDisksState *state;
432   gboolean     finished;
433   GCond        cond;
434   GMutex       data_mutex;
435 } UDisksStateCheckSyncData;
436 
437 static gboolean
udisks_state_check_sync_func(UDisksStateCheckSyncData * data)438 udisks_state_check_sync_func (UDisksStateCheckSyncData *data)
439 {
440   udisks_state_check_in_thread (data->state);
441 
442   /* signal the calling thread the cleanup has finished */
443   g_mutex_lock (&data->data_mutex);
444   data->finished = TRUE;
445   g_cond_signal (&data->cond);
446   g_mutex_unlock (&data->data_mutex);
447 
448   return FALSE;
449 }
450 
451 /**
452  * udisks_state_check_sync:
453  * @state: A #UDisksState.
454  *
455  * Causes the clean-up thread for @state to check if anything should be cleaned up and perform the cleanup.
456  *
457  * This can be called from any thread and in contrast to udisks_state_check() will block the calling thread until cleanup is finished.
458  */
459 void
udisks_state_check_sync(UDisksState * state)460 udisks_state_check_sync (UDisksState *state)
461 {
462   UDisksStateCheckSyncData data = {0, };
463 
464   g_return_if_fail (UDISKS_IS_STATE (state));
465   g_return_if_fail (state->thread != NULL);
466 
467   g_cond_init (&data.cond);
468   g_mutex_init (&data.data_mutex);
469   data.state = state;
470   data.finished = FALSE;
471 
472   g_mutex_lock (&data.data_mutex);
473   g_main_context_invoke (state->context,
474                          (GSourceFunc) udisks_state_check_sync_func,
475                          &data);
476 
477   /* wait for the mainloop running in the cleanup thread to process our injected task */
478   while (!data.finished)
479     g_cond_wait (&data.cond, &data.data_mutex);
480   g_mutex_unlock (&data.data_mutex);
481 
482   g_cond_clear (&data.cond);
483   g_mutex_clear (&data.data_mutex);
484 }
485 
486 /**
487  * udisks_state_check_block:
488  * @state: A #UDisksState.
489  * @block_device: Device number of the block device to check.
490  *
491  * Performs cleanup of a mounted filesystem over a single block device. In case
492  * the clean-up thread is busy, waits until the current iteration is finished.
493  *
494  * This can be called from any thread and will block the calling thread until
495  * cleanup is finished. Note that this ignores #UDisksLinuxBlockObject cleanup lock
496  * that is supposed to be held by the caller.
497  */
498 void
udisks_state_check_block(UDisksState * state,dev_t block_device)499 udisks_state_check_block (UDisksState   *state,
500                           dev_t          block_device)
501 {
502 
503   g_mutex_lock (&state->lock);
504 
505   udisks_state_check_mounted_fs (state,
506                                  UDISKS_STATE_FILE_MOUNTED_FS,
507                                  NULL,
508                                  block_device);
509   udisks_state_check_mounted_fs (state,
510                                  UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT,
511                                  NULL,
512                                  block_device);
513 
514   g_mutex_unlock (&state->lock);
515 }
516 
517 /**
518  * udisks_state_get_daemon:
519  * @state: A #UDisksState.
520  *
521  * Gets the daemon used by @state.
522  *
523  * Returns: A #UDisksDaemon. Do not free, the object is owned by @state.
524  */
525 UDisksDaemon *
udisks_state_get_daemon(UDisksState * state)526 udisks_state_get_daemon (UDisksState *state)
527 {
528   g_return_val_if_fail (UDISKS_IS_STATE (state), NULL);
529   return state->daemon;
530 }
531 
532 /* ---------------------------------------------------------------------------------------------------- */
533 
534 /* must be called from state thread */
535 static void
udisks_state_check_in_thread(UDisksState * state)536 udisks_state_check_in_thread (UDisksState *state)
537 {
538   GArray *devs_to_clean;
539 
540   g_mutex_lock (&state->lock);
541 
542   /* We have to do a two-stage clean-up since fake block devices
543    * can't be stopped if they are in use
544    */
545 
546   udisks_info ("Cleanup check start");
547 
548   /* First go through all block devices we might tear down
549    * but only check + record devices marked for cleaning
550    */
551   devs_to_clean = g_array_new (FALSE, FALSE, sizeof (dev_t));
552   udisks_state_check_unlocked_crypto_dev (state,
553                                           TRUE, /* check_only */
554                                           devs_to_clean);
555   udisks_state_check_loop (state,
556                            TRUE, /* check_only */
557                            devs_to_clean);
558 
559   udisks_state_check_mdraid (state,
560                              TRUE, /* check_only */
561                              devs_to_clean);
562 
563   /* Then go through all mounted filesystems and pass the
564    * devices that we intend to clean...
565    */
566   udisks_state_check_mounted_fs (state,
567                                  UDISKS_STATE_FILE_MOUNTED_FS,
568                                  devs_to_clean,
569                                  0);
570   udisks_state_check_mounted_fs (state,
571                                  UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT,
572                                  devs_to_clean,
573                                  0);
574 
575   /* Then go through all block devices and clear them up
576    * ... for real this time
577    */
578   udisks_state_check_unlocked_crypto_dev (state,
579                                           FALSE, /* check_only */
580                                           NULL);
581   udisks_state_check_loop (state,
582                            FALSE, /* check_only */
583                            NULL);
584 
585   udisks_state_check_mdraid (state,
586                              FALSE, /* check_only */
587                              NULL);
588 
589   g_array_free (devs_to_clean, TRUE);
590 
591   udisks_info ("Cleanup check end");
592 
593   g_mutex_unlock (&state->lock);
594 }
595 
596 /* ---------------------------------------------------------------------------------------------------- */
597 
598 static GVariant *
lookup_asv(GVariant * asv,const gchar * key)599 lookup_asv (GVariant    *asv,
600             const gchar *key)
601 {
602   GVariantIter iter;
603   const gchar *iter_key;
604   GVariant *value;
605   GVariant *ret;
606 
607   ret = NULL;
608 
609   g_variant_iter_init (&iter, asv);
610   while (g_variant_iter_next (&iter,
611                               "{&s@v}",
612                               &iter_key,
613                               &value))
614     {
615       if (g_strcmp0 (key, iter_key) == 0)
616         {
617           ret = g_variant_get_variant (value);
618           g_variant_unref (value);
619           goto out;
620         }
621       g_variant_unref (value);
622     }
623 
624  out:
625   return ret;
626 }
627 
628 /* ---------------------------------------------------------------------------------------------------- */
629 
630 static void
trigger_change_uevent(const gchar * sysfs_path)631 trigger_change_uevent (const gchar *sysfs_path)
632 {
633   gchar* path = NULL;
634   gint fd = -1;
635 
636   g_return_if_fail (sysfs_path != NULL);
637 
638   path = g_strconcat (sysfs_path, "/uevent", NULL);
639   fd = open (path, O_WRONLY);
640   if (fd < 0)
641     {
642       udisks_warning ("Error opening %s for triggering change uevent: %m", path);
643       goto out;
644     }
645 
646   if (write (fd, "change", sizeof "change" - 1) != sizeof "change" - 1)
647     {
648       udisks_warning ("Error writing 'change' to file %s: %m", path);
649       goto out;
650     }
651 
652  out:
653   if (fd >= 0)
654     close (fd);
655   g_free (path);
656 }
657 
658 /* returns TRUE if the entry should be kept */
659 static gboolean
udisks_state_check_mounted_fs_entry(UDisksState * state,GVariant * value,GArray * devs_to_clean,dev_t match_block_device)660 udisks_state_check_mounted_fs_entry (UDisksState  *state,
661                                      GVariant     *value,
662                                      GArray       *devs_to_clean,
663                                      dev_t         match_block_device)
664 {
665   const gchar *mount_point_str;
666   gchar mount_point[PATH_MAX] = { '\0', };
667   GVariant *details;
668   GVariant *block_device_value;
669   dev_t block_device = 0;
670   GVariant *fstab_mount_value;
671   gboolean fstab_mount;
672   gboolean keep;
673   gchar *s;
674   GList *mounts;
675   GList *l;
676   gboolean is_mounted;
677   gboolean device_exists;
678   gboolean device_to_be_cleaned;
679   UDisksMountMonitor *monitor;
680   GUdevClient *udev_client;
681   GUdevDevice *udev_device;
682   guint n;
683   gchar *change_sysfs_path = NULL;
684   UDisksObject *block_object = NULL;
685   gboolean locked;
686 
687   keep = FALSE;
688   is_mounted = FALSE;
689   device_exists = FALSE;
690   device_to_be_cleaned = FALSE;
691   block_device_value = NULL;
692   fstab_mount_value = NULL;
693   fstab_mount = FALSE;
694   details = NULL;
695   locked = FALSE;
696 
697   monitor = udisks_daemon_get_mount_monitor (state->daemon);
698 
699   g_variant_get (value,
700                  "{&s@a{sv}}",
701                  &mount_point_str,
702                  &details);
703 
704   block_device_value = lookup_asv (details, "block-device");
705   if (block_device_value == NULL)
706     {
707       s = g_variant_print (value, TRUE);
708       udisks_critical ("udisks_state_check_mounted_fs_entry: mounted-fs entry %s is invalid: no block-device key/value pair", s);
709       g_free (s);
710       goto out;
711     }
712   block_device = g_variant_get_uint64 (block_device_value);
713 
714   if (match_block_device != 0 && match_block_device != block_device)
715     {
716       /* not intended for this block device, continue with parent iteration */
717       keep = TRUE;
718       goto out;
719     }
720 
721   block_object = udisks_daemon_find_block (state->daemon, block_device);
722   /* skip locking if called from udisks_state_check_block() */
723   if (block_object != NULL && match_block_device == 0)
724     {
725       if (! udisks_linux_block_object_try_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (block_object)))
726         {
727           gchar *device_file;
728 
729           device_file = udisks_linux_block_object_get_device_file (UDISKS_LINUX_BLOCK_OBJECT (block_object));
730           udisks_debug ("udisks_state_check_mounted_fs_entry: block device %s is busy, skipping cleanup", device_file);
731           g_free (device_file);
732 
733           keep = TRUE;
734           goto out;
735         }
736       locked = TRUE;
737     }
738 
739   if (realpath (mount_point_str, mount_point) == NULL)
740     {
741       udisks_critical ("udisks_state_check_mounted_fs_entry: mountpoint %s is invalid, cannot recover the canonical path ", mount_point_str);
742     }
743 
744   fstab_mount_value = lookup_asv (details, "fstab-mount");
745   if (fstab_mount_value == NULL)
746     {
747       s = g_variant_print (value, TRUE);
748       udisks_critical ("udisks_state_check_mounted_fs_entry: mounted-fs entry %s is invalid: no fstab-mount key/value pair", s);
749       g_free (s);
750       goto out;
751     }
752   fstab_mount = g_variant_get_boolean (fstab_mount_value);
753 
754   /* udisks_debug ("Validating mounted-fs entry for mount point %s", mount_point); */
755 
756   /* Figure out if still mounted */
757   mounts = udisks_mount_monitor_get_mounts_for_dev (monitor, block_device);
758   for (l = mounts; l != NULL; l = l->next)
759     {
760       UDisksMount *mount = UDISKS_MOUNT (l->data);
761       if (udisks_mount_get_mount_type (mount) == UDISKS_MOUNT_TYPE_FILESYSTEM &&
762           g_strcmp0 (udisks_mount_get_mount_path (mount), mount_point) == 0)
763         {
764           is_mounted = TRUE;
765           break;
766         }
767     }
768   g_list_free_full (mounts, g_object_unref);
769 
770   /* Figure out if block device still exists */
771   udev_client = udisks_linux_provider_get_udev_client (udisks_daemon_get_linux_provider (state->daemon));
772   udev_device = g_udev_client_query_by_device_number (udev_client,
773                                                       G_UDEV_DEVICE_TYPE_BLOCK,
774                                                       block_device);
775   if (udev_device != NULL)
776     {
777       /* If media is pulled from a device with removable media (say,
778        * /dev/sdc being a CF reader connected via USB) and a device
779        * (say, /dev/sdc1) on the media is mounted, the kernel won't
780        * necessarily send 'remove' uevent for /dev/sdc1 even though
781        * media removal was detected (we will get a 'change' uevent
782        * though).
783        *
784        * Therefore, we need to sanity-check the device - it appears
785        * that it's good enough to just check the 'size' sysfs
786        * attribute of the device (or its enclosing device if a
787        * partition)
788        *
789        * Additionally, if we conclude that the device is not valid
790        * (e.g. still there but size of device or its enclosing device
791        * is 0), we also need to poke the kernel (via a 'change'
792        * uevent) to make the device go away. We do that after
793        * unmounting the device.
794        */
795 
796       /* if umounting, issue 'change' event on the device after unmounting it */
797       change_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (udev_device));
798 
799       if (g_udev_device_get_sysfs_attr_as_uint64 (udev_device, "size") > 0)
800         {
801           /* for partition, also check enclosing device */
802           if (g_strcmp0 (g_udev_device_get_devtype (udev_device), "partition") == 0)
803             {
804               GUdevDevice *udev_device_disk;
805               udev_device_disk = g_udev_device_get_parent_with_subsystem (udev_device, "block", "disk");
806               if (udev_device_disk != NULL)
807                 {
808                   if (g_udev_device_get_sysfs_attr_as_uint64 (udev_device_disk, "size") > 0)
809                     {
810                       device_exists = TRUE;
811                     }
812                   /* if unmounting, issue 'change' uevent on the enclosing device after unmounting the device */
813                   g_free (change_sysfs_path);
814                   change_sysfs_path = g_strdup (g_udev_device_get_sysfs_path (udev_device_disk));
815                   g_object_unref (udev_device_disk);
816                 }
817             }
818           else
819             {
820               device_exists = TRUE;
821             }
822         }
823       g_object_unref (udev_device);
824     }
825 
826   /* Figure out if the device is about to be cleaned up */
827   if (devs_to_clean != NULL)
828     for (n = 0; n < devs_to_clean->len; n++)
829       {
830         dev_t dev_to_clean = g_array_index (devs_to_clean, dev_t, n);
831         if (dev_to_clean == block_device)
832           {
833             device_to_be_cleaned = TRUE;
834             break;
835           }
836       }
837 
838   if (is_mounted && device_exists && !device_to_be_cleaned)
839     keep = TRUE;
840 
841  out:
842 
843   if (!keep)
844     {
845       if (!device_exists)
846         {
847           udisks_notice ("Cleaning up mount point %s (device %u:%u no longer exists)",
848                          mount_point, major (block_device), minor (block_device));
849         }
850       else if (device_to_be_cleaned)
851         {
852           udisks_notice ("Cleaning up mount point %s (device %u:%u is about to be cleaned up)",
853                          mount_point, major (block_device), minor (block_device));
854         }
855       else if (!is_mounted)
856         {
857           udisks_notice ("Cleaning up mount point %s (device %u:%u is not mounted)",
858                          mount_point, major (block_device), minor (block_device));
859         }
860 
861       if (is_mounted)
862         {
863           gchar *escaped_mount_point;
864           gchar *error_message;
865 
866           error_message = NULL;
867           escaped_mount_point = g_shell_quote (mount_point);
868           /* right now -l is the only way to "force unmount" file systems... */
869           if (!udisks_daemon_launch_spawned_job_sync (state->daemon,
870                                                       NULL, /* UDisksObject */
871                                                       "cleanup", 0, /* StartedByUID */
872                                                       NULL, /* GCancellable */
873                                                       0,    /* uid_t run_as_uid */
874                                                       0,    /* uid_t run_as_euid */
875                                                       NULL, /* gint *out_status */
876                                                       &error_message,
877                                                       NULL,  /* input_string */
878                                                       "umount -l %s",
879                                                       escaped_mount_point))
880             {
881               udisks_critical ("Error cleaning up mount point %s: Error unmounting: %s",
882                             mount_point, error_message);
883               g_free (escaped_mount_point);
884               g_free (error_message);
885               /* keep the entry so we can clean it up later */
886               keep = TRUE;
887               goto out2;
888             }
889           g_free (escaped_mount_point);
890           g_free (error_message);
891 
892           /* just unmounting the device does not make the kernel revalidate media
893            * so we issue a 'change' uevent to request that
894            */
895           if (change_sysfs_path != NULL)
896             {
897               trigger_change_uevent (change_sysfs_path);
898             }
899         }
900 
901       /* remove directory */
902       if (!fstab_mount)
903         {
904           if (g_file_test (mount_point, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
905             {
906               if (g_rmdir (mount_point) != 0)
907                 {
908                   udisks_critical ("Error cleaning up mount point %s: Error removing directory: %m",
909                                 mount_point);
910                   /* keep the entry so we can clean it up later */
911                   keep = TRUE;
912                   goto out2;
913                 }
914             }
915         }
916     }
917 
918  out2:
919   if (fstab_mount_value != NULL)
920     g_variant_unref (fstab_mount_value);
921   if (block_device_value != NULL)
922     g_variant_unref (block_device_value);
923   if (details != NULL)
924     g_variant_unref (details);
925   if (locked)
926     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (block_object));
927   g_clear_object (&block_object);
928 
929   g_free (change_sysfs_path);
930 
931   return keep;
932 }
933 
934 /* called with mutex->lock held */
935 static void
udisks_state_check_mounted_fs(UDisksState * state,const gchar * key,GArray * devs_to_clean,dev_t match_block_device)936 udisks_state_check_mounted_fs (UDisksState *state,
937                                const gchar *key,
938                                GArray      *devs_to_clean,
939                                dev_t        match_block_device)
940 {
941   gboolean changed;
942   GVariant *value;
943   GVariant *new_value;
944   GVariantBuilder builder;
945 
946   changed = FALSE;
947 
948   /* load existing entries */
949   value = udisks_state_get (state,
950                             key,
951                             G_VARIANT_TYPE ("a{sa{sv}}"));
952 
953   /* check valid entries */
954   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sa{sv}}"));
955   if (value != NULL)
956     {
957       GVariantIter iter;
958       GVariant *child;
959       g_variant_iter_init (&iter, value);
960       while ((child = g_variant_iter_next_value (&iter)) != NULL)
961         {
962           if (udisks_state_check_mounted_fs_entry (state, child, devs_to_clean, match_block_device))
963             g_variant_builder_add_value (&builder, child);
964           else
965             changed = TRUE;
966           g_variant_unref (child);
967         }
968       g_variant_unref (value);
969     }
970 
971   new_value = g_variant_builder_end (&builder);
972 
973   /* save new entries */
974   if (changed)
975     {
976       udisks_state_set (state,
977                         key,
978                         G_VARIANT_TYPE ("a{sa{sv}}"),
979                         new_value /* consumes new_value */);
980     }
981   else
982     {
983       g_variant_unref (new_value);
984     }
985 }
986 
987 /* ---------------------------------------------------------------------------------------------------- */
988 
989 /**
990  * udisks_state_add_mounted_fs:
991  * @state: A #UDisksState.
992  * @block_device: The block device.
993  * @mount_point: The mount point.
994  * @uid: The user id of the process requesting the device to be mounted.
995  * @fstab_mount: %TRUE if the device was mounted via /etc/fstab.
996  * @persistent: %TRUE if the mount point is on a persistent filesystem.
997  *
998  * Adds a new entry to the
999  * <filename>/run/udisks2/mounted-fs</filename> file.
1000  */
1001 void
udisks_state_add_mounted_fs(UDisksState * state,const gchar * mount_point,dev_t block_device,uid_t uid,gboolean fstab_mount,gboolean persistent)1002 udisks_state_add_mounted_fs (UDisksState    *state,
1003                              const gchar    *mount_point,
1004                              dev_t           block_device,
1005                              uid_t           uid,
1006                              gboolean        fstab_mount,
1007                              gboolean        persistent)
1008 {
1009   GVariant *value;
1010   GVariant *new_value;
1011   GVariant *details_value;
1012   GVariantBuilder builder;
1013   GVariantBuilder details_builder;
1014 
1015   g_return_if_fail (UDISKS_IS_STATE (state));
1016   g_return_if_fail (mount_point != NULL);
1017 
1018   g_mutex_lock (&state->lock);
1019 
1020   /* load existing entries */
1021   value = udisks_state_get (state,
1022                             persistent ? UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT : UDISKS_STATE_FILE_MOUNTED_FS,
1023                             G_VARIANT_TYPE ("a{sa{sv}}"));
1024 
1025   /* start by including existing entries */
1026   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sa{sv}}"));
1027   if (value != NULL)
1028     {
1029       GVariantIter iter;
1030       GVariant *child;
1031 
1032       g_variant_iter_init (&iter, value);
1033       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1034         {
1035           const gchar *entry_mount_point;
1036           g_variant_get (child, "{&s@a{sv}}", &entry_mount_point, NULL);
1037           /* Skip/remove stale entries */
1038           if (g_strcmp0 (entry_mount_point, mount_point) == 0)
1039             {
1040               udisks_warning ("Removing stale entry for mount point `%s' in /run/udisks/mounted-fs file",
1041                               entry_mount_point);
1042             }
1043           else
1044             {
1045               g_variant_builder_add_value (&builder, child);
1046             }
1047           g_variant_unref (child);
1048         }
1049       g_variant_unref (value);
1050     }
1051 
1052   /* build the details */
1053   g_variant_builder_init (&details_builder, G_VARIANT_TYPE ("a{sv}"));
1054   g_variant_builder_add (&details_builder,
1055                          "{sv}",
1056                          "block-device",
1057                          g_variant_new_uint64 (block_device));
1058   g_variant_builder_add (&details_builder,
1059                          "{sv}",
1060                          "mounted-by-uid",
1061                          g_variant_new_uint32 (uid));
1062   g_variant_builder_add (&details_builder,
1063                          "{sv}",
1064                          "fstab-mount",
1065                          g_variant_new_boolean (fstab_mount));
1066   details_value = g_variant_builder_end (&details_builder);
1067 
1068   /* finally add the new entry */
1069   g_variant_builder_add (&builder,
1070                          "{s@a{sv}}",
1071                          mount_point,
1072                          details_value); /* consumes details_value */
1073   new_value = g_variant_builder_end (&builder);
1074 
1075   /* save new entries */
1076   udisks_state_set (state,
1077                     persistent ? UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT : UDISKS_STATE_FILE_MOUNTED_FS,
1078                     G_VARIANT_TYPE ("a{sa{sv}}"),
1079                     new_value /* consumes new_value */);
1080 
1081   g_mutex_unlock (&state->lock);
1082 }
1083 
1084 /* called with state->lock held */
1085 static gchar *
find_mounted_fs_for_key(UDisksState * state,const gchar * key,dev_t block_device,uid_t * out_uid,gboolean * out_fstab_mount)1086 find_mounted_fs_for_key (UDisksState   *state,
1087                          const gchar   *key,
1088                          dev_t          block_device,
1089                          uid_t         *out_uid,
1090                          gboolean      *out_fstab_mount)
1091 {
1092   gchar *ret = NULL;
1093   GVariant *value;
1094 
1095   /* load existing entries */
1096   value = udisks_state_get (state,
1097                             key,
1098                             G_VARIANT_TYPE ("a{sa{sv}}"));
1099 
1100   /* look through list */
1101   if (value != NULL)
1102     {
1103       GVariantIter iter;
1104       GVariant *child;
1105       g_variant_iter_init (&iter, value);
1106       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1107         {
1108           const gchar *mount_point;
1109           GVariant *details;
1110           GVariant *block_device_value;
1111 
1112           g_variant_get (child,
1113                          "{&s@a{sv}}",
1114                          &mount_point,
1115                          &details);
1116 
1117           block_device_value = lookup_asv (details, "block-device");
1118           if (block_device_value != NULL)
1119             {
1120               dev_t iter_block_device;
1121               iter_block_device = g_variant_get_uint64 (block_device_value);
1122               if (iter_block_device == block_device)
1123                 {
1124                   ret = g_strdup (mount_point);
1125                   if (out_uid != NULL)
1126                     {
1127                       GVariant *lookup_value;
1128                       lookup_value = lookup_asv (details, "mounted-by-uid");
1129                       *out_uid = 0;
1130                       if (lookup_value != NULL)
1131                         {
1132                           *out_uid = g_variant_get_uint32 (lookup_value);
1133                           g_variant_unref (lookup_value);
1134                         }
1135                     }
1136                   if (out_fstab_mount != NULL)
1137                     {
1138                       GVariant *lookup_value;
1139                       lookup_value = lookup_asv (details, "fstab-mount");
1140                       *out_fstab_mount = FALSE;
1141                       if (lookup_value != NULL)
1142                         {
1143                           *out_fstab_mount = g_variant_get_boolean (lookup_value);
1144                           g_variant_unref (lookup_value);
1145                         }
1146                     }
1147                   g_variant_unref (block_device_value);
1148                   g_variant_unref (details);
1149                   g_variant_unref (child);
1150                   goto out;
1151                 }
1152               g_variant_unref (block_device_value);
1153             }
1154           g_variant_unref (details);
1155           g_variant_unref (child);
1156         }
1157     }
1158 
1159  out:
1160   if (value != NULL)
1161     g_variant_unref (value);
1162   return ret;
1163 }
1164 
1165 /**
1166  * udisks_state_find_mounted_fs:
1167  * @state: A #UDisksState.
1168  * @block_device: The block device.
1169  * @out_uid: Return location for the user id who mounted the device or %NULL.
1170  * @out_fstab_mount: Return location for whether the device was a fstab mount or %NULL.
1171  *
1172  * Gets the mount point for @block_device, if it exists in the
1173  * <filename>/run/udisks2/mounted-fs</filename> file.
1174  *
1175  * Returns: The mount point for @block_device or %NULL if not found.
1176  */
1177 gchar *
udisks_state_find_mounted_fs(UDisksState * state,dev_t block_device,uid_t * out_uid,gboolean * out_fstab_mount)1178 udisks_state_find_mounted_fs (UDisksState   *state,
1179                               dev_t          block_device,
1180                               uid_t         *out_uid,
1181                               gboolean      *out_fstab_mount)
1182 {
1183   gchar *ret;
1184 
1185   g_return_val_if_fail (UDISKS_IS_STATE (state), NULL);
1186 
1187   g_mutex_lock (&state->lock);
1188 
1189   ret = find_mounted_fs_for_key (state,
1190                                  UDISKS_STATE_FILE_MOUNTED_FS,
1191                                  block_device,
1192                                  out_uid,
1193                                  out_fstab_mount);
1194   if (ret == NULL)
1195     ret = find_mounted_fs_for_key (state,
1196                                    UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT,
1197                                    block_device,
1198                                    out_uid,
1199                                    out_fstab_mount);
1200 
1201   g_mutex_unlock (&state->lock);
1202 
1203   return ret;
1204 }
1205 /* ---------------------------------------------------------------------------------------------------- */
1206 
1207 /* returns TRUE if the entry should be kept */
1208 static gboolean
udisks_state_check_unlocked_crypto_dev_entry(UDisksState * state,GVariant * value,gboolean check_only,GArray * devs_to_clean)1209 udisks_state_check_unlocked_crypto_dev_entry (UDisksState  *state,
1210                                               GVariant     *value,
1211                                               gboolean      check_only,
1212                                               GArray       *devs_to_clean)
1213 {
1214   guint64 cleartext_device;
1215   GVariant *details;
1216   GVariant *crypto_device_value;
1217   dev_t crypto_device;
1218   GVariant *dm_uuid_value;
1219   const gchar *dm_uuid;
1220   gchar *device_file_cleartext;
1221   gboolean keep;
1222   gchar *s;
1223   gboolean is_unlocked;
1224   gboolean crypto_device_exists;
1225   gboolean attempt_no_cleanup;
1226   GUdevClient *udev_client;
1227   GUdevDevice *udev_cleartext_device;
1228   GUdevDevice *udev_crypto_device;
1229 
1230   keep = FALSE;
1231   is_unlocked = FALSE;
1232   crypto_device_exists = FALSE;
1233   attempt_no_cleanup = FALSE;
1234   device_file_cleartext = NULL;
1235   crypto_device_value = NULL;
1236   dm_uuid_value = NULL;
1237   details = NULL;
1238 
1239   udev_client = udisks_linux_provider_get_udev_client (udisks_daemon_get_linux_provider (state->daemon));
1240 
1241   g_variant_get (value,
1242                  "{t@a{sv}}",
1243                  &cleartext_device,
1244                  &details);
1245 
1246   crypto_device_value = lookup_asv (details, "crypto-device");
1247   if (crypto_device_value == NULL)
1248     {
1249       s = g_variant_print (value, TRUE);
1250       udisks_critical ("unlocked-crypto-dev entry %s is invalid: no crypto-device key/value pair", s);
1251       g_free (s);
1252       attempt_no_cleanup = TRUE;
1253       goto out;
1254     }
1255   crypto_device = g_variant_get_uint64 (crypto_device_value);
1256 
1257   dm_uuid_value = lookup_asv (details, "dm-uuid");
1258   if (dm_uuid_value == NULL)
1259     {
1260       s = g_variant_print (value, TRUE);
1261       udisks_critical ("unlocked-crypto-dev entry %s is invalid: no dm-uuid key/value pair", s);
1262       g_free (s);
1263       attempt_no_cleanup = TRUE;
1264       goto out;
1265     }
1266   dm_uuid = g_variant_get_bytestring (dm_uuid_value);
1267 
1268   /*udisks_debug ("Validating luks entry for device %d:%d (backed by %d:%d) with uuid %s",
1269                 major (cleartext_device), minor (cleartext_device),
1270                 major (crypto_device), minor (crypto_device), dm_uuid);*/
1271 
1272   udev_cleartext_device = g_udev_client_query_by_device_number (udev_client,
1273                                                                 G_UDEV_DEVICE_TYPE_BLOCK,
1274                                                                 cleartext_device);
1275   if (udev_cleartext_device != NULL)
1276     {
1277       const gchar *current_dm_uuid;
1278       device_file_cleartext = g_strdup (g_udev_device_get_device_file (udev_cleartext_device));
1279       current_dm_uuid = g_udev_device_get_sysfs_attr (udev_cleartext_device, "dm/uuid");
1280       /* if the UUID doesn't match, then the dm device might have been reused... */
1281       if (g_strcmp0 (current_dm_uuid, dm_uuid) != 0)
1282         {
1283           s = g_variant_print (value, TRUE);
1284           udisks_warning ("Removing unlocked-crypto-dev entry %s because %s now has another dm-uuid %s",
1285                           s, device_file_cleartext,
1286                           current_dm_uuid != NULL ? current_dm_uuid : "(NULL)");
1287           g_free (s);
1288           attempt_no_cleanup = TRUE;
1289         }
1290       else
1291         {
1292           is_unlocked = TRUE;
1293         }
1294       g_object_unref (udev_cleartext_device);
1295     }
1296 
1297   udev_crypto_device = g_udev_client_query_by_device_number (udev_client,
1298                                                              G_UDEV_DEVICE_TYPE_BLOCK,
1299                                                              crypto_device);
1300   if (udev_crypto_device != NULL)
1301     {
1302       crypto_device_exists = TRUE;
1303       g_object_unref (udev_crypto_device);
1304     }
1305 
1306   /* OK, entry is valid - keep it around */
1307   if (is_unlocked && crypto_device_exists)
1308     keep = TRUE;
1309 
1310  out:
1311 
1312   if (check_only && !keep)
1313     {
1314       dev_t cleartext_device_dev_t = cleartext_device; /* !@#!$# array type */
1315       g_array_append_val (devs_to_clean, cleartext_device_dev_t);
1316       keep = TRUE;
1317       goto out2;
1318     }
1319 
1320   if (!keep && !attempt_no_cleanup)
1321     {
1322       if (is_unlocked)
1323         {
1324           CryptoJobData data;
1325           GError *error = NULL;
1326 
1327           udisks_notice ("Cleaning up LUKS device %s (backing device %u:%u no longer exists)",
1328                          device_file_cleartext,
1329                          major (crypto_device), minor (crypto_device));
1330 
1331           data.map_name = device_file_cleartext;
1332           if (!udisks_daemon_launch_threaded_job_sync (state->daemon,
1333                                                        NULL, /* UDisksObject */
1334                                                        "cleanup",
1335                                                        0, /* StartedByUID */
1336                                                        luks_close_job_func,
1337                                                        &data,
1338                                                        NULL, /* user_data_free_func */
1339                                                        NULL, /* cancellable */
1340                                                        &error))
1341             {
1342               udisks_critical ("Error cleaning up LUKS device %s: %s",
1343                                device_file_cleartext, error->message);
1344               g_clear_error (&error);
1345               /* keep the entry so we can clean it up later */
1346               keep = TRUE;
1347               goto out2;
1348             }
1349         }
1350       else
1351         {
1352           udisks_notice ("LUKS device %u:%u was manually removed",
1353                          major (cleartext_device), minor (cleartext_device));
1354         }
1355     }
1356 
1357  out2:
1358   g_free (device_file_cleartext);
1359   if (crypto_device_value != NULL)
1360     g_variant_unref (crypto_device_value);
1361   if (dm_uuid_value != NULL)
1362     g_variant_unref (dm_uuid_value);
1363   if (details != NULL)
1364     g_variant_unref (details);
1365   return keep;
1366 }
1367 
1368 /* called with mutex->lock held */
1369 static void
udisks_state_check_unlocked_crypto_dev(UDisksState * state,gboolean check_only,GArray * devs_to_clean)1370 udisks_state_check_unlocked_crypto_dev (UDisksState *state,
1371                                         gboolean     check_only,
1372                                         GArray      *devs_to_clean)
1373 {
1374   gboolean changed;
1375   GVariant *value;
1376   GVariant *new_value;
1377   GVariantBuilder builder;
1378 
1379   changed = FALSE;
1380 
1381   /* load existing entries */
1382   value = udisks_state_get (state,
1383                             UDISKS_STATE_FILE_UNLOCKED_CRYPTO_DEV,
1384                             G_VARIANT_TYPE ("a{ta{sv}}"));
1385 
1386   /* check valid entries */
1387   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ta{sv}}"));
1388   if (value != NULL)
1389     {
1390       GVariantIter iter;
1391       GVariant *child;
1392       g_variant_iter_init (&iter, value);
1393       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1394         {
1395           if (udisks_state_check_unlocked_crypto_dev_entry (state, child, check_only, devs_to_clean))
1396             g_variant_builder_add_value (&builder, child);
1397           else
1398             changed = TRUE;
1399           g_variant_unref (child);
1400         }
1401       g_variant_unref (value);
1402     }
1403 
1404   new_value = g_variant_builder_end (&builder);
1405 
1406   /* save new entries */
1407   if (changed)
1408     {
1409       udisks_state_set (state,
1410                         UDISKS_STATE_FILE_UNLOCKED_CRYPTO_DEV,
1411                         G_VARIANT_TYPE ("a{ta{sv}}"),
1412                         new_value /* consumes new_value */);
1413     }
1414   else
1415     {
1416       g_variant_unref (new_value);
1417     }
1418 }
1419 
1420 /* ---------------------------------------------------------------------------------------------------- */
1421 
1422 /**
1423  * udisks_state_add_unlocked_crypto_dev:
1424  * @state: A #UDisksState.
1425  * @cleartext_device: The clear-text device.
1426  * @crypto_device: The crypto device.
1427  * @dm_uuid: The UUID of the unlocked dm device.
1428  * @uid: The user id of the process requesting the device to be unlocked.
1429  *
1430  * Adds a new entry to the
1431  * <filename>/run/udisks2/unlocked-crypto-dev</filename> file.
1432  */
1433 void
udisks_state_add_unlocked_crypto_dev(UDisksState * state,dev_t cleartext_device,dev_t crypto_device,const gchar * dm_uuid,uid_t uid)1434 udisks_state_add_unlocked_crypto_dev (UDisksState  *state,
1435                                       dev_t         cleartext_device,
1436                                       dev_t         crypto_device,
1437                                       const gchar  *dm_uuid,
1438                                       uid_t         uid)
1439 {
1440   GVariant *value;
1441   GVariant *new_value;
1442   GVariant *details_value;
1443   GVariantBuilder builder;
1444   GVariantBuilder details_builder;
1445 
1446   g_return_if_fail (UDISKS_IS_STATE (state));
1447   g_return_if_fail (dm_uuid != NULL);
1448 
1449   g_mutex_lock (&state->lock);
1450 
1451   /* load existing entries */
1452   value = udisks_state_get (state,
1453                             UDISKS_STATE_FILE_UNLOCKED_CRYPTO_DEV,
1454                             G_VARIANT_TYPE ("a{ta{sv}}"));
1455 
1456   /* start by including existing entries */
1457   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ta{sv}}"));
1458   if (value != NULL)
1459     {
1460       GVariantIter iter;
1461       GVariant *child;
1462       g_variant_iter_init (&iter, value);
1463       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1464         {
1465           guint64 entry_cleartext_device;
1466           g_variant_get (child, "{t@a{sv}}", &entry_cleartext_device, NULL);
1467           /* Skip/remove stale entries */
1468           if ((dev_t) entry_cleartext_device == cleartext_device)
1469             {
1470               udisks_warning ("Removing stale entry for cleartext device %d:%d in /run/udisks2/unlocked-crypto-dev file",
1471                               (gint) major (entry_cleartext_device),
1472                               (gint) minor (entry_cleartext_device));
1473             }
1474           else
1475             {
1476               g_variant_builder_add_value (&builder, child);
1477             }
1478           g_variant_unref (child);
1479         }
1480       g_variant_unref (value);
1481     }
1482 
1483   /* build the details */
1484   g_variant_builder_init (&details_builder, G_VARIANT_TYPE ("a{sv}"));
1485   g_variant_builder_add (&details_builder,
1486                          "{sv}",
1487                          "crypto-device",
1488                          g_variant_new_uint64 (crypto_device));
1489   g_variant_builder_add (&details_builder,
1490                          "{sv}",
1491                          "dm-uuid",
1492                          g_variant_new_bytestring (dm_uuid));
1493   g_variant_builder_add (&details_builder,
1494                          "{sv}",
1495                          "unlocked-by-uid",
1496                          g_variant_new_uint32 (uid));
1497   details_value = g_variant_builder_end (&details_builder);
1498 
1499   /* finally add the new entry */
1500   g_variant_builder_add (&builder,
1501                          "{t@a{sv}}",
1502                          (guint64) cleartext_device,
1503                          details_value); /* consumes details_value */
1504   new_value = g_variant_builder_end (&builder);
1505 
1506   /* save new entries */
1507   udisks_state_set (state,
1508                     UDISKS_STATE_FILE_UNLOCKED_CRYPTO_DEV,
1509                     G_VARIANT_TYPE ("a{ta{sv}}"),
1510                     new_value /* consumes new_value */);
1511 
1512   g_mutex_unlock (&state->lock);
1513 }
1514 
1515 /**
1516  * udisks_state_find_unlocked_crypto_dev:
1517  * @state: A #UDisksState.
1518  * @crypto_device: The block device.
1519  * @out_uid: Return location for the user id who mounted the device or %NULL.
1520  *
1521  * Gets the clear-text device for @crypto_device, if it exists in the
1522  * <filename>/run/udisks2/unlocked-crypto-dev</filename> file.
1523  *
1524  * Returns: The cleartext device for @crypto_device or 0 if not found.
1525  */
1526 dev_t
udisks_state_find_unlocked_crypto_dev(UDisksState * state,dev_t crypto_device,uid_t * out_uid)1527 udisks_state_find_unlocked_crypto_dev (UDisksState   *state,
1528                                        dev_t          crypto_device,
1529                                        uid_t         *out_uid)
1530 {
1531   dev_t ret;
1532   GVariant *value;
1533 
1534   g_return_val_if_fail (UDISKS_IS_STATE (state), 0);
1535 
1536   g_mutex_lock (&state->lock);
1537 
1538   ret = 0;
1539   value = NULL;
1540 
1541   /* load existing entries */
1542   value = udisks_state_get (state,
1543                             UDISKS_STATE_FILE_UNLOCKED_CRYPTO_DEV,
1544                             G_VARIANT_TYPE ("a{ta{sv}}"));
1545 
1546   /* look through list */
1547   if (value != NULL)
1548     {
1549       GVariantIter iter;
1550       GVariant *child;
1551       g_variant_iter_init (&iter, value);
1552       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1553         {
1554           guint64 cleartext_device;
1555           GVariant *details;
1556           GVariant *crypto_device_value;
1557 
1558           g_variant_get (child,
1559                          "{t@a{sv}}",
1560                          &cleartext_device,
1561                          &details);
1562 
1563           crypto_device_value = lookup_asv (details, "crypto-device");
1564           if (crypto_device_value != NULL)
1565             {
1566               dev_t iter_crypto_device;
1567               iter_crypto_device = g_variant_get_uint64 (crypto_device_value);
1568               if (iter_crypto_device == crypto_device)
1569                 {
1570                   ret = cleartext_device;
1571                   if (out_uid != NULL)
1572                     {
1573                       GVariant *lookup_value;
1574                       lookup_value = lookup_asv (details, "unlocked-by-uid");
1575                       *out_uid = 0;
1576                       if (lookup_value != NULL)
1577                         {
1578                           *out_uid = g_variant_get_uint32 (lookup_value);
1579                           g_variant_unref (lookup_value);
1580                         }
1581                     }
1582                   g_variant_unref (crypto_device_value);
1583                   g_variant_unref (details);
1584                   g_variant_unref (child);
1585                   goto out;
1586                 }
1587               g_variant_unref (crypto_device_value);
1588             }
1589           g_variant_unref (details);
1590           g_variant_unref (child);
1591         }
1592     }
1593 
1594  out:
1595   if (value != NULL)
1596     g_variant_unref (value);
1597   g_mutex_unlock (&state->lock);
1598   return ret;
1599 }
1600 
1601 /* ---------------------------------------------------------------------------------------------------- */
1602 
1603 /* returns TRUE if the entry should be kept */
1604 static gboolean
udisks_state_check_loop_entry(UDisksState * state,GVariant * value,gboolean check_only,GArray * devs_to_clean)1605 udisks_state_check_loop_entry (UDisksState  *state,
1606                                GVariant     *value,
1607                                gboolean      check_only,
1608                                GArray       *devs_to_clean)
1609 {
1610   const gchar *loop_device;
1611   GVariant *details = NULL;
1612   gboolean keep = FALSE;
1613   GVariant *backing_file_value = NULL;
1614   const gchar *backing_file;
1615   GUdevClient *udev_client;
1616   GUdevDevice *device = NULL;
1617   const gchar *sysfs_backing_file;
1618 
1619   udev_client = udisks_linux_provider_get_udev_client (udisks_daemon_get_linux_provider (state->daemon));
1620 
1621   g_variant_get (value,
1622                  "{&s@a{sv}}",
1623                  &loop_device,
1624                  &details);
1625 
1626   backing_file_value = lookup_asv (details, "backing-file");
1627   if (backing_file_value == NULL)
1628     {
1629       gchar *s;
1630       s = g_variant_print (value, TRUE);
1631       udisks_critical ("udisks_state_check_loop_entry: loop entry %s is invalid: no backing-file key/value pair", s);
1632       g_free (s);
1633       goto out;
1634     }
1635   backing_file = g_variant_get_bytestring (backing_file_value);
1636 
1637   /* check the loop device is still set up */
1638   device = g_udev_client_query_by_device_file (udev_client, loop_device);
1639   if (device == NULL)
1640     {
1641       udisks_info ("udisks_state_check_loop_entry: no udev device for %s", loop_device);
1642       goto out;
1643     }
1644   if (g_udev_device_get_sysfs_attr (device, "loop/offset") == NULL)
1645     {
1646       udisks_info ("udisks_state_check_loop_entry: loop device %s is not setup  (no loop/offset sysfs file)", loop_device);
1647       goto out;
1648     }
1649 
1650   /* Check the loop device set up, is the one that _we_ set up
1651    *
1652    * Note that drivers/block/loop.c:loop_attr_backing_file_show() uses d_path()
1653    * on lo_file_name so in the event that the underlying fs was unmounted
1654    * (just 'umount -l /path/to/fs/holding/backing/file to try) it cuts
1655    * off the mount path.... in this case we simply just give up managing
1656    * the loop device
1657    */
1658   sysfs_backing_file = g_udev_device_get_sysfs_attr (device, "loop/backing_file");
1659   if (g_strcmp0 (sysfs_backing_file, backing_file) != 0)
1660     {
1661       udisks_notice ("udisks_state_check_loop_entry: unexpected name for %s - expected `%s' but got `%s'",
1662                      loop_device, backing_file, sysfs_backing_file);
1663       goto out;
1664     }
1665 
1666   /* OK, entry is valid - keep it around */
1667   keep = TRUE;
1668 
1669  out:
1670 
1671   if (check_only && !keep)
1672     {
1673       if (device != NULL)
1674         {
1675           dev_t dev_number = g_udev_device_get_device_number (device);
1676           g_array_append_val (devs_to_clean, dev_number);
1677         }
1678       keep = TRUE;
1679       goto out2;
1680     }
1681 
1682   if (!keep)
1683     {
1684       udisks_notice ("No longer watching loop device %s", loop_device);
1685     }
1686 
1687  out2:
1688   g_clear_object (&device);
1689   if (backing_file_value != NULL)
1690     g_variant_unref (backing_file_value);
1691   if (details != NULL)
1692     g_variant_unref (details);
1693   return keep;
1694 }
1695 
1696 static void
udisks_state_check_loop(UDisksState * state,gboolean check_only,GArray * devs_to_clean)1697 udisks_state_check_loop (UDisksState *state,
1698                          gboolean     check_only,
1699                          GArray      *devs_to_clean)
1700 {
1701   gboolean changed;
1702   GVariant *value;
1703   GVariant *new_value;
1704   GVariantBuilder builder;
1705 
1706   changed = FALSE;
1707 
1708   /* load existing entries */
1709   value = udisks_state_get (state,
1710                             UDISKS_STATE_FILE_LOOP,
1711                             G_VARIANT_TYPE ("a{sa{sv}}"));
1712 
1713   /* check valid entries */
1714   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sa{sv}}"));
1715   if (value != NULL)
1716     {
1717       GVariantIter iter;
1718       GVariant *child;
1719       g_variant_iter_init (&iter, value);
1720       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1721         {
1722           if (udisks_state_check_loop_entry (state, child, check_only, devs_to_clean))
1723             g_variant_builder_add_value (&builder, child);
1724           else
1725             changed = TRUE;
1726           g_variant_unref (child);
1727         }
1728       g_variant_unref (value);
1729     }
1730 
1731   new_value = g_variant_builder_end (&builder);
1732 
1733   /* save new entries */
1734   if (changed)
1735     {
1736       udisks_state_set (state,
1737                         UDISKS_STATE_FILE_LOOP,
1738                         G_VARIANT_TYPE ("a{sa{sv}}"),
1739                         new_value /* consumes new_value */);
1740     }
1741   else
1742     {
1743       g_variant_unref (new_value);
1744     }
1745 }
1746 
1747 /* ---------------------------------------------------------------------------------------------------- */
1748 
1749 /**
1750  * udisks_state_add_loop:
1751  * @state: A #UDisksState.
1752  * @device_file: The loop device file.
1753  * @backing_file: The backing file.
1754  * @backing_file_device: The #dev_t of the backing file or 0 if unknown.
1755  * @uid: The user id of the process requesting the loop device.
1756  *
1757  * Adds a new entry to the <filename>/run/udisks2/loop</filename>
1758  * file.
1759  */
1760 void
udisks_state_add_loop(UDisksState * state,const gchar * device_file,const gchar * backing_file,dev_t backing_file_device,uid_t uid)1761 udisks_state_add_loop (UDisksState   *state,
1762                        const gchar   *device_file,
1763                        const gchar   *backing_file,
1764                        dev_t          backing_file_device,
1765                        uid_t          uid)
1766 {
1767   GVariant *value;
1768   GVariant *new_value;
1769   GVariant *details_value;
1770   GVariantBuilder builder;
1771   GVariantBuilder details_builder;
1772 
1773   g_return_if_fail (UDISKS_IS_STATE (state));
1774   g_return_if_fail (device_file != NULL);
1775   g_return_if_fail (backing_file != NULL);
1776 
1777   g_mutex_lock (&state->lock);
1778 
1779   /* load existing entries */
1780   value = udisks_state_get (state,
1781                             UDISKS_STATE_FILE_LOOP,
1782                             G_VARIANT_TYPE ("a{sa{sv}}"));
1783 
1784   /* start by including existing entries */
1785   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sa{sv}}"));
1786   if (value != NULL)
1787     {
1788       GVariantIter iter;
1789       GVariant *child;
1790       g_variant_iter_init (&iter, value);
1791       while ((child = g_variant_iter_next_value (&iter)) != NULL)
1792         {
1793           const gchar *entry_loop_device;
1794           g_variant_get (child, "{&s@a{sv}}", &entry_loop_device, NULL);
1795           /* Skip/remove stale entries */
1796           if (g_strcmp0 (entry_loop_device, device_file) == 0)
1797             {
1798               udisks_warning ("Removing stale entry for loop device `%s' in /run/udisks2/loop file",
1799                               entry_loop_device);
1800             }
1801           else
1802             {
1803               g_variant_builder_add_value (&builder, child);
1804             }
1805           g_variant_unref (child);
1806         }
1807       g_variant_unref (value);
1808     }
1809 
1810   /* build the details */
1811   g_variant_builder_init (&details_builder, G_VARIANT_TYPE ("a{sv}"));
1812   g_variant_builder_add (&details_builder,
1813                          "{sv}",
1814                          "backing-file",
1815                          g_variant_new_bytestring (backing_file));
1816   g_variant_builder_add (&details_builder,
1817                          "{sv}",
1818                          "backing-file-device",
1819                          g_variant_new_uint64 (backing_file_device));
1820   g_variant_builder_add (&details_builder,
1821                          "{sv}",
1822                          "setup-by-uid",
1823                          g_variant_new_uint32 (uid));
1824   details_value = g_variant_builder_end (&details_builder);
1825 
1826   /* finally add the new entry */
1827   g_variant_builder_add (&builder,
1828                          "{s@a{sv}}",
1829                          device_file,
1830                          details_value); /* consumes details_value */
1831   new_value = g_variant_builder_end (&builder);
1832 
1833   /* save new entries */
1834   udisks_state_set (state,
1835                     UDISKS_STATE_FILE_LOOP,
1836                     G_VARIANT_TYPE ("a{sa{sv}}"),
1837                     new_value /* consumes new_value */);
1838 
1839   g_mutex_unlock (&state->lock);
1840 }
1841 
1842 /**
1843  * node_cb:
1844  * @child:          Element in the list you are visiting
1845  * @compare_data:   Data used for comparison
1846  * @user_data:      Data supplied by call to iterate list
1847  *
1848  * Returns: %TRUE if the iteration should stop, else %FALSE
1849  */
1850 typedef gboolean (*node_cb)(GVariant *child, gpointer compare_data,
1851                             gpointer user_data);
1852 
1853 
1854 /**
1855  * iterate_list:
1856  * @list:           The list to iterate over
1857  * @visit:          The function called on each element in the list
1858  * @compare_data:   Data used for comparison
1859  * @user_data:      Pointer to user supplied data
1860  *
1861  * Returns: %TRUE if iteration was stopped by node_cb, else %FALSE
1862  */
1863 static gboolean
iterate_list(GVariant * list,node_cb visit,gpointer compare_data,gpointer user_data)1864 iterate_list (GVariant *list, node_cb visit, gpointer compare_data,
1865               gpointer user_data)
1866 {
1867   gboolean rc = FALSE;
1868   GVariantIter iter;
1869   GVariant *child = NULL;
1870 
1871   if (!list)
1872     return rc;
1873 
1874   g_variant_iter_init (&iter, list);
1875 
1876   while (!rc && ((child = g_variant_iter_next_value (&iter)) != NULL))
1877     {
1878       rc = visit (child, compare_data, user_data);
1879       g_variant_unref (child);
1880     }
1881 
1882   return rc;
1883 }
1884 
1885 static gboolean
_udisks_state_has_loop_list_visitor(GVariant * child,gpointer compare_data,gpointer user_data)1886 _udisks_state_has_loop_list_visitor (GVariant *child, gpointer compare_data,
1887                                      gpointer user_data )
1888 {
1889   gboolean ret = FALSE;
1890   const gchar *iter_device_file = NULL;
1891   GVariant *details = NULL;
1892   uid_t *out_uid = (uid_t *) user_data;
1893 
1894   g_variant_get (child, "{&s@a{sv}}", &iter_device_file, &details);
1895 
1896   if (g_strcmp0 (iter_device_file, ((gchar*)compare_data)) == 0)
1897     {
1898       if (out_uid != NULL)
1899         {
1900           GVariant *lookup_value;
1901           lookup_value = lookup_asv (details, "setup-by-uid");
1902           *out_uid = 0;
1903           if (lookup_value != NULL)
1904             {
1905               *out_uid = g_variant_get_uint32 (lookup_value);
1906               g_variant_unref (lookup_value);
1907               ret = TRUE;
1908             }
1909         }
1910     }
1911     g_variant_unref (details);
1912 
1913     return ret;
1914 }
1915 
1916 /**
1917  * udisks_state_has_loop:
1918  * @state: A #UDisksState
1919  * @device_file: A loop device file.
1920  * @out_uid: Return location for the user id who setup the loop device or %NULL.
1921  *
1922  * Checks if @device_file is set up via udisks.
1923  *
1924  * Returns: %TRUE if set up via udisks, otherwise %FALSE or if @error is set.
1925  */
1926 gboolean
udisks_state_has_loop(UDisksState * state,const gchar * device_file,uid_t * out_uid)1927 udisks_state_has_loop (UDisksState   *state,
1928                        const gchar   *device_file,
1929                        uid_t         *out_uid)
1930 {
1931   gboolean ret;
1932   GVariant *value;
1933 
1934   g_return_val_if_fail (UDISKS_IS_STATE (state), FALSE);
1935 
1936   g_mutex_lock (&state->lock);
1937 
1938   ret = 0;
1939   value = NULL;
1940 
1941   /* load existing entries */
1942   value = udisks_state_get (state,
1943                             UDISKS_STATE_FILE_LOOP,
1944                             G_VARIANT_TYPE ("a{sa{sv}}"));
1945   if (value != NULL)
1946    {
1947       ret = iterate_list (value,
1948                           _udisks_state_has_loop_list_visitor,
1949                           (gpointer) device_file, (gpointer) out_uid);
1950       g_variant_unref (value);
1951    }
1952 
1953   g_mutex_unlock (&state->lock);
1954   return ret;
1955 }
1956 
1957 /* ---------------------------------------------------------------------------------------------------- */
1958 
1959 /* returns TRUE if the entry should be kept */
1960 static gboolean
udisks_state_check_mdraid_entry(UDisksState * state,GVariant * value,gboolean check_only,GArray * devs_to_clean)1961 udisks_state_check_mdraid_entry (UDisksState  *state,
1962                                  GVariant     *value,
1963                                  gboolean      check_only,
1964                                  GArray       *devs_to_clean)
1965 {
1966   dev_t raid_device;
1967   GVariant *details = NULL;
1968   gboolean keep = FALSE;
1969   GUdevClient *udev_client;
1970   GUdevDevice *device = NULL;
1971   const gchar *array_state;
1972 
1973   udev_client = udisks_linux_provider_get_udev_client (udisks_daemon_get_linux_provider (state->daemon));
1974 
1975   g_variant_get (value,
1976                  "{t@a{sv}}",
1977                  &raid_device,
1978                  &details);
1979 
1980   /* check if the RAID device is still set up */
1981   device = g_udev_client_query_by_device_number (udev_client, G_UDEV_DEVICE_TYPE_BLOCK, raid_device);
1982   if (device == NULL)
1983     {
1984       udisks_info ("udisks_state_check_mdraid_entry: no udev device for raid device %u:%u", major (raid_device), minor (raid_device));
1985       goto out;
1986     }
1987   array_state = g_udev_device_get_sysfs_attr (device, "md/array_state");
1988   if (array_state == NULL)
1989     {
1990       udisks_info ("udisks_state_check_mdraid_entry: raid device %u:%u is not setup  (no md/array_state sysfs file)",
1991                    major (raid_device), minor (raid_device));
1992       goto out;
1993     }
1994 
1995   if (g_strcmp0 (array_state, "clear") == 0)
1996     {
1997       /* 'clear' means that the array is not set up any more */
1998       goto out;
1999     }
2000 
2001   /* OK, entry is valid - keep it around */
2002   keep = TRUE;
2003 
2004  out:
2005 
2006   if (check_only && !keep)
2007     {
2008       if (device != NULL)
2009         {
2010           g_array_append_val (devs_to_clean, raid_device);
2011         }
2012       keep = TRUE;
2013       goto out2;
2014     }
2015 
2016   if (!keep)
2017     {
2018       udisks_notice ("No longer watching mdraid device %u:%u", major (raid_device), minor (raid_device));
2019     }
2020 
2021  out2:
2022   g_clear_object (&device);
2023   if (details != NULL)
2024     g_variant_unref (details);
2025   return keep;
2026 }
2027 
2028 static void
udisks_state_check_mdraid(UDisksState * state,gboolean check_only,GArray * devs_to_clean)2029 udisks_state_check_mdraid (UDisksState *state,
2030                            gboolean     check_only,
2031                            GArray      *devs_to_clean)
2032 {
2033   gboolean changed;
2034   GVariant *value;
2035   GVariant *new_value;
2036   GVariantBuilder builder;
2037 
2038   changed = FALSE;
2039 
2040   /* load existing entries */
2041   value = udisks_state_get (state,
2042                             UDISKS_STATE_FILE_MDRAID,
2043                             G_VARIANT_TYPE ("a{ta{sv}}"));
2044 
2045   /* check valid entries */
2046   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ta{sv}}"));
2047   if (value != NULL)
2048     {
2049       GVariantIter iter;
2050       GVariant *child;
2051       g_variant_iter_init (&iter, value);
2052       while ((child = g_variant_iter_next_value (&iter)) != NULL)
2053         {
2054           if (udisks_state_check_mdraid_entry (state, child, check_only, devs_to_clean))
2055             g_variant_builder_add_value (&builder, child);
2056           else
2057             changed = TRUE;
2058           g_variant_unref (child);
2059         }
2060       g_variant_unref (value);
2061     }
2062 
2063   new_value = g_variant_builder_end (&builder);
2064 
2065   /* save new entries */
2066   if (changed)
2067     {
2068       udisks_state_set (state,
2069                         UDISKS_STATE_FILE_MDRAID,
2070                         G_VARIANT_TYPE ("a{ta{sv}}"),
2071                         new_value /* consumes new_value */);
2072     }
2073   else
2074     {
2075       g_variant_unref (new_value);
2076     }
2077 }
2078 
2079 /**
2080  * udisks_state_add_mdraid:
2081  * @state: A #UDisksState.
2082  * @raid_device: The #dev_t for the RAID device.
2083  * @uid: The user id of the process requesting the loop device.
2084  *
2085  * Adds a new entry to the <filename>/run/udisks2/mdraid</filename>
2086  * file.
2087  */
2088 void
udisks_state_add_mdraid(UDisksState * state,dev_t raid_device,uid_t uid)2089 udisks_state_add_mdraid (UDisksState   *state,
2090                          dev_t          raid_device,
2091                          uid_t          uid)
2092 {
2093   GVariant *value;
2094   GVariant *new_value;
2095   GVariant *details_value;
2096   GVariantBuilder builder;
2097   GVariantBuilder details_builder;
2098 
2099   g_return_if_fail (UDISKS_IS_STATE (state));
2100 
2101   g_mutex_lock (&state->lock);
2102 
2103   /* load existing entries */
2104   value = udisks_state_get (state,
2105                             UDISKS_STATE_FILE_MDRAID,
2106                             G_VARIANT_TYPE ("a{ta{sv}}"));
2107 
2108   /* start by including existing entries */
2109   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ta{sv}}"));
2110   if (value != NULL)
2111     {
2112       GVariantIter iter;
2113       GVariant *child;
2114       g_variant_iter_init (&iter, value);
2115       while ((child = g_variant_iter_next_value (&iter)) != NULL)
2116         {
2117           guint64 entry_raid_device;
2118           g_variant_get (child, "{t@a{sv}}", &entry_raid_device, NULL);
2119           /* Skip/remove stale entries */
2120           if (entry_raid_device == raid_device)
2121             {
2122               udisks_warning ("Removing stale entry for raid device %u:%u in /run/udisks2/mdraid file",
2123                                 major (raid_device), minor (raid_device));
2124             }
2125           else
2126             {
2127               g_variant_builder_add_value (&builder, child);
2128             }
2129           g_variant_unref (child);
2130         }
2131       g_variant_unref (value);
2132     }
2133 
2134   /* build the details */
2135   g_variant_builder_init (&details_builder, G_VARIANT_TYPE ("a{sv}"));
2136   g_variant_builder_add (&details_builder,
2137                          "{sv}",
2138                          "started-by-uid",
2139                          g_variant_new_uint32 (uid));
2140   details_value = g_variant_builder_end (&details_builder);
2141 
2142   /* finally add the new entry */
2143   g_variant_builder_add (&builder,
2144                          "{t@a{sv}}",
2145                          raid_device,
2146                          details_value); /* consumes details_value */
2147   new_value = g_variant_builder_end (&builder);
2148 
2149   /* save new entries */
2150   udisks_state_set (state,
2151                     UDISKS_STATE_FILE_MDRAID,
2152                     G_VARIANT_TYPE ("a{ta{sv}}"),
2153                     new_value /* consumes new_value */);
2154 
2155   g_mutex_unlock (&state->lock);
2156 }
2157 
2158 static gboolean
_udisks_state_has_mdraid_list_visitor(GVariant * child,gpointer compare_data,gpointer user_data)2159 _udisks_state_has_mdraid_list_visitor (GVariant *child, gpointer compare_data,
2160                                        gpointer user_data )
2161 {
2162   gboolean ret = FALSE;
2163   guint64 iter_raid_device;
2164   GVariant *details;
2165   dev_t *raid_device = (dev_t*) compare_data;
2166   uid_t *out_uid = (uid_t*) user_data;
2167 
2168   g_variant_get (child, "{t@a{sv}}", &iter_raid_device, &details);
2169 
2170   if (iter_raid_device == *raid_device)
2171     {
2172       ret = TRUE;
2173       if (out_uid != NULL)
2174         {
2175           GVariant *lookup_value;
2176           lookup_value = lookup_asv (details, "started-by-uid");
2177           *out_uid = 0;
2178           if (lookup_value != NULL)
2179             {
2180               *out_uid = g_variant_get_uint32 (lookup_value);
2181               g_variant_unref (lookup_value);
2182             }
2183         }
2184       g_variant_unref (details);
2185     }
2186   return ret;
2187 }
2188 
2189 /**
2190  * udisks_state_has_mdraid:
2191  * @state: A #UDisksState
2192  * @raid_device: A #dev_t for the RAID device.
2193  * @out_uid: Return location for the user id who setup the loop device or %NULL.
2194  *
2195  * Checks if @raid_device is set up via udisks.
2196  *
2197  * Returns: %TRUE if set up via udisks, otherwise %FALSE or if @error is set.
2198  */
2199 gboolean
udisks_state_has_mdraid(UDisksState * state,dev_t raid_device,uid_t * out_uid)2200 udisks_state_has_mdraid (UDisksState   *state,
2201                          dev_t          raid_device,
2202                          uid_t         *out_uid)
2203 {
2204   gboolean ret = FALSE;
2205   GVariant *value = NULL;
2206 
2207   g_return_val_if_fail (UDISKS_IS_STATE (state), FALSE);
2208 
2209   g_mutex_lock (&state->lock);
2210 
2211   /* load existing entries */
2212   value = udisks_state_get (state,
2213                             UDISKS_STATE_FILE_MDRAID,
2214                             G_VARIANT_TYPE ("a{ta{sv}}"));
2215   if (value != NULL)
2216     {
2217       ret = iterate_list (value, _udisks_state_has_mdraid_list_visitor,
2218                           (gpointer) &raid_device, (gpointer) out_uid);
2219       g_variant_unref (value);
2220     }
2221 
2222   g_mutex_unlock (&state->lock);
2223   return ret;
2224 }
2225 
2226 /* ---------------------------------------------------------------------------------------------------- */
2227 
2228 /**
2229  * udisks_state_add_module:
2230  * @state: A #UDisksState.
2231  * @module_name: The #UDisksModule name.
2232  *
2233  * Adds a new entry to the <filename>/run/udisks2/modules</filename>
2234  * file. The @module_name string should be the one returned by
2235  * udisks_module_get_name().
2236  */
2237 void
udisks_state_add_module(UDisksState * state,const gchar * module_name)2238 udisks_state_add_module (UDisksState *state,
2239                          const gchar *module_name)
2240 {
2241   GVariant *value;
2242   GVariant *new_value;
2243   GVariantBuilder builder;
2244 
2245   g_return_if_fail (UDISKS_IS_STATE (state));
2246 
2247   g_mutex_lock (&state->lock);
2248 
2249   /* load existing entries */
2250   value = udisks_state_get (state,
2251                             UDISKS_STATE_FILE_MODULES,
2252                             G_VARIANT_TYPE ("a{sa{sv}}"));
2253 
2254   /* start by including existing entries */
2255   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sa{sv}}"));
2256   if (value != NULL)
2257     {
2258       GVariantIter iter;
2259       GVariant *child;
2260       g_variant_iter_init (&iter, value);
2261       while ((child = g_variant_iter_next_value (&iter)) != NULL)
2262         {
2263           const gchar *entry_module_name;
2264           g_variant_get (child, "{&s@a{sv}}", &entry_module_name, NULL);
2265           /* Skip/remove stale entries */
2266           if (g_strcmp0 (entry_module_name, module_name) == 0)
2267             {
2268               udisks_warning ("Removing stale entry for module '%s' in /run/udisks2/modules file",
2269                               entry_module_name);
2270             }
2271           else
2272             {
2273               g_variant_builder_add_value (&builder, child);
2274             }
2275           g_variant_unref (child);
2276         }
2277       g_variant_unref (value);
2278     }
2279 
2280   /* finally add the new entry */
2281   g_variant_builder_add (&builder,
2282                          "{s@a{sv}}",
2283                          module_name,
2284                          g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
2285   new_value = g_variant_builder_end (&builder);
2286 
2287   /* save new entries */
2288   udisks_state_set (state,
2289                     UDISKS_STATE_FILE_MODULES,
2290                     G_VARIANT_TYPE ("a{sa{sv}}"),
2291                     new_value /* consumes new_value */);
2292 
2293   g_mutex_unlock (&state->lock);
2294 }
2295 
2296 /**
2297  * udisks_state_clear_modules:
2298  * @state: A #UDisksState.
2299  *
2300  * Removes all entries from the <filename>/run/udisks2/modules</filename>
2301  * state file.
2302  */
2303 void
udisks_state_clear_modules(UDisksState * state)2304 udisks_state_clear_modules (UDisksState *state)
2305 {
2306   gchar *path;
2307 
2308   g_return_if_fail (UDISKS_IS_STATE (state));
2309 
2310   g_mutex_lock (&state->lock);
2311 
2312   /* just remove the file entirely */
2313   path = get_state_file_path (UDISKS_STATE_FILE_MODULES);
2314   if (g_unlink (path))
2315     {
2316       if (errno != ENOENT)
2317         g_warning ("Error removing state file %s: %m", path);
2318     }
2319   g_free (path);
2320 
2321   g_mutex_unlock (&state->lock);
2322 }
2323 
2324 /**
2325  * udisks_state_get_modules:
2326  * @state: A #UDisksState
2327  *
2328  * Retrieves list of modules recorded in the <filename>/run/udisks2/modules</filename>
2329  * state file.
2330  *
2331  * Returns: (transfer full) (array zero-terminated=1): A list of module names. Free with g_strfreev().
2332  */
2333 gchar **
udisks_state_get_modules(UDisksState * state)2334 udisks_state_get_modules (UDisksState *state)
2335 {
2336   GPtrArray *list;
2337   GVariant *value;
2338 
2339   g_return_val_if_fail (UDISKS_IS_STATE (state), NULL);
2340 
2341   g_mutex_lock (&state->lock);
2342 
2343   list = g_ptr_array_new ();
2344 
2345   value = udisks_state_get (state,
2346                             UDISKS_STATE_FILE_MODULES,
2347                             G_VARIANT_TYPE ("a{sa{sv}}"));
2348   if (value != NULL)
2349     {
2350       GVariantIter iter;
2351       GVariant *child;
2352       g_variant_iter_init (&iter, value);
2353       while ((child = g_variant_iter_next_value (&iter)) != NULL)
2354         {
2355           gchar *entry_module_name;
2356           g_variant_get (child, "{s@a{sv}}", &entry_module_name, NULL);
2357           g_ptr_array_add (list, entry_module_name);
2358           g_variant_unref (child);
2359         }
2360       g_variant_unref (value);
2361     }
2362 
2363   g_mutex_unlock (&state->lock);
2364 
2365   g_ptr_array_add (list, NULL);
2366 
2367   return (gchar **) g_ptr_array_free (list, FALSE);
2368 }
2369 
2370 
2371 /* ---------------------------------------------------------------------------------------------------- */
2372 
2373 /* returns fully allocated string */
2374 static gchar *
get_state_file_path(const gchar * key)2375 get_state_file_path (const gchar *key)
2376 {
2377   if (g_str_equal (key, UDISKS_STATE_FILE_MOUNTED_FS_PERSISTENT))
2378     return g_strdup_printf (PACKAGE_LOCALSTATE_DIR "/lib/udisks2/%s", key);
2379 
2380   return g_strdup_printf ("/run/udisks2/%s", key);
2381 }
2382 
2383 static GVariant *
udisks_state_get(UDisksState * state,const gchar * key,const GVariantType * type)2384 udisks_state_get (UDisksState           *state,
2385                   const gchar           *key,
2386                   const GVariantType    *type)
2387 {
2388   gchar *path;
2389   GVariant *ret;
2390   gchar *contents = NULL;
2391   GError *local_error = NULL;
2392   gsize length = 0;
2393 
2394   g_return_val_if_fail (UDISKS_IS_STATE (state), NULL);
2395   g_return_val_if_fail (key != NULL, NULL);
2396   g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
2397 
2398   /* TODO:
2399    *
2400    * - could use a cache here to avoid loading files all the time
2401    * - could also mmap the file
2402    */
2403 
2404   path = get_state_file_path (key);
2405 
2406   /* see if it's already in the cache */
2407   ret = g_hash_table_lookup (state->cache, path);
2408   if (ret != NULL)
2409     {
2410       g_variant_ref (ret);
2411       goto out;
2412     }
2413 
2414   if (!g_file_get_contents (path,
2415                             &contents,
2416                             &length,
2417                             &local_error))
2418     {
2419       if (local_error->domain == G_FILE_ERROR && local_error->code == G_FILE_ERROR_NOENT)
2420         {
2421           /* this is not an error */
2422           g_clear_error (&local_error);
2423           goto out;
2424         }
2425 
2426       udisks_warning ("Error getting state data %s: %s (%s, %d)",
2427                       key,
2428                       local_error->message,
2429                       g_quark_to_string (local_error->domain),
2430                       local_error->code);
2431       g_clear_error (&local_error);
2432       goto out;
2433     }
2434 
2435   ret = g_variant_new_from_data (type,
2436                                  (gconstpointer) contents,
2437                                  length,
2438                                  FALSE,
2439                                  g_free,
2440                                  contents);
2441   g_warn_if_fail (ret != NULL);
2442   g_variant_ref_sink (ret);
2443 
2444   contents = NULL; /* ownership transferred to the returned GVariant */
2445 
2446  out:
2447   g_free (contents);
2448   g_free (path);
2449   return ret;
2450 }
2451 
2452 static gboolean
udisks_state_set(UDisksState * state,const gchar * key,const GVariantType * type,GVariant * value)2453 udisks_state_set (UDisksState          *state,
2454                   const gchar          *key,
2455                   const GVariantType   *type,
2456                   GVariant             *value)
2457 {
2458   gboolean ret = FALSE;
2459   gsize size;
2460   gchar *path;
2461   gchar *data;
2462   GVariant *normalized;
2463   GError *error = NULL;
2464 
2465   g_return_val_if_fail (UDISKS_IS_STATE (state), FALSE);
2466   g_return_val_if_fail (key != NULL, FALSE);
2467   g_return_val_if_fail (g_variant_type_is_definite (type), FALSE);
2468   g_return_val_if_fail (g_variant_is_of_type (value, type), FALSE);
2469 
2470   g_variant_ref_sink (value);
2471   normalized = g_variant_get_normal_form (value);
2472   size = g_variant_get_size (normalized);
2473   data = g_malloc (size);
2474   g_variant_store (normalized, data);
2475 
2476   path = get_state_file_path (key);
2477   g_hash_table_insert (state->cache, g_strdup (path), g_variant_ref (value));
2478 
2479   if (!g_file_set_contents (path,
2480                             data,
2481                             size,
2482                             &error))
2483     {
2484       udisks_warning ("Error setting state data %s: %s (%s, %d)", key,
2485                      error->message,
2486                      g_quark_to_string (error->domain),
2487                      error->code);
2488       g_clear_error (&error);
2489       goto out;
2490     }
2491 
2492   ret = TRUE;
2493 
2494  out:
2495 
2496   g_free (path);
2497   g_free (data);
2498   g_variant_unref (normalized);
2499   g_variant_unref (value);
2500   return ret;
2501 }
2502 
2503 /* ---------------------------------------------------------------------------------------------------- */
2504