~behda/+junk/udisks2.original

« back to all changes in this revision

Viewing changes to src/udisksstate.c

  • Committer: behda
  • Date: 2014-05-24 15:15:11 UTC
  • Revision ID: pauvitk@gmail.com-20140524151511-3vtr0uubjewx3z2j
Initial commit of source code and Debian packaging.

Show diffs side-by-side

added added

removed removed

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