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