~behda/+junk/udisks2.original

« back to all changes in this revision

Viewing changes to src/udiskslinuxmanager.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) 2007-2010 David Zeuthen <zeuthen@gmail.com>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include "config.h"
 
22
#include <glib/gi18n-lib.h>
 
23
#include <gio/gunixfdlist.h>
 
24
#include <glib/gstdio.h>
 
25
 
 
26
#include <sys/types.h>
 
27
#include <sys/stat.h>
 
28
#include <fcntl.h>
 
29
#include <sys/ioctl.h>
 
30
 
 
31
#include <pwd.h>
 
32
#include <grp.h>
 
33
#include <errno.h>
 
34
#include <string.h>
 
35
#include <stdlib.h>
 
36
 
 
37
#include <linux/loop.h>
 
38
 
 
39
#include "udiskslogging.h"
 
40
#include "udiskslinuxmanager.h"
 
41
#include "udisksdaemon.h"
 
42
#include "udisksdaemonutil.h"
 
43
#include "udisksstate.h"
 
44
#include "udiskslinuxblockobject.h"
 
45
#include "udiskslinuxdevice.h"
 
46
 
 
47
/**
 
48
 * SECTION:udiskslinuxmanager
 
49
 * @title: UDisksLinuxManager
 
50
 * @short_description: Linux implementation of #UDisksManager
 
51
 *
 
52
 * This type provides an implementation of the #UDisksManager
 
53
 * interface on Linux.
 
54
 */
 
55
 
 
56
typedef struct _UDisksLinuxManagerClass   UDisksLinuxManagerClass;
 
57
 
 
58
/**
 
59
 * UDisksLinuxManager:
 
60
 *
 
61
 * The #UDisksLinuxManager structure contains only private data and should
 
62
 * only be accessed using the provided API.
 
63
 */
 
64
struct _UDisksLinuxManager
 
65
{
 
66
  UDisksManagerSkeleton parent_instance;
 
67
 
 
68
  GMutex lock;
 
69
 
 
70
  UDisksDaemon *daemon;
 
71
};
 
72
 
 
73
struct _UDisksLinuxManagerClass
 
74
{
 
75
  UDisksManagerSkeletonClass parent_class;
 
76
};
 
77
 
 
78
enum
 
79
{
 
80
  PROP_0,
 
81
  PROP_DAEMON
 
82
};
 
83
 
 
84
static void manager_iface_init (UDisksManagerIface *iface);
 
85
 
 
86
G_DEFINE_TYPE_WITH_CODE (UDisksLinuxManager, udisks_linux_manager, UDISKS_TYPE_MANAGER_SKELETON,
 
87
                         G_IMPLEMENT_INTERFACE (UDISKS_TYPE_MANAGER, manager_iface_init));
 
88
 
 
89
/* ---------------------------------------------------------------------------------------------------- */
 
90
 
 
91
static void
 
92
udisks_linux_manager_finalize (GObject *object)
 
93
{
 
94
  UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
 
95
 
 
96
  g_mutex_clear (&(manager->lock));
 
97
 
 
98
  G_OBJECT_CLASS (udisks_linux_manager_parent_class)->finalize (object);
 
99
}
 
100
 
 
101
static void
 
102
udisks_linux_manager_get_property (GObject    *object,
 
103
                                   guint       prop_id,
 
104
                                   GValue     *value,
 
105
                                   GParamSpec *pspec)
 
106
{
 
107
  UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
 
108
 
 
109
  switch (prop_id)
 
110
    {
 
111
    case PROP_DAEMON:
 
112
      g_value_set_object (value, udisks_linux_manager_get_daemon (manager));
 
113
      break;
 
114
 
 
115
    default:
 
116
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
117
      break;
 
118
    }
 
119
}
 
120
 
 
121
static void
 
122
udisks_linux_manager_set_property (GObject      *object,
 
123
                                   guint         prop_id,
 
124
                                   const GValue *value,
 
125
                                   GParamSpec   *pspec)
 
126
{
 
127
  UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
 
128
 
 
129
  switch (prop_id)
 
130
    {
 
131
    case PROP_DAEMON:
 
132
      g_assert (manager->daemon == NULL);
 
133
      /* we don't take a reference to the daemon */
 
134
      manager->daemon = g_value_get_object (value);
 
135
      break;
 
136
 
 
137
    default:
 
138
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
139
      break;
 
140
    }
 
141
}
 
142
 
 
143
static void
 
144
udisks_linux_manager_init (UDisksLinuxManager *manager)
 
145
{
 
146
  g_mutex_init (&(manager->lock));
 
147
  g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (manager),
 
148
                                       G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
 
149
}
 
150
 
 
151
static void
 
152
udisks_linux_manager_class_init (UDisksLinuxManagerClass *klass)
 
153
{
 
154
  GObjectClass *gobject_class;
 
155
 
 
156
  gobject_class = G_OBJECT_CLASS (klass);
 
157
  gobject_class->finalize     = udisks_linux_manager_finalize;
 
158
  gobject_class->set_property = udisks_linux_manager_set_property;
 
159
  gobject_class->get_property = udisks_linux_manager_get_property;
 
160
 
 
161
  /**
 
162
   * UDisksLinuxManager:daemon:
 
163
   *
 
164
   * The #UDisksDaemon for the object.
 
165
   */
 
166
  g_object_class_install_property (gobject_class,
 
167
                                   PROP_DAEMON,
 
168
                                   g_param_spec_object ("daemon",
 
169
                                                        "Daemon",
 
170
                                                        "The daemon for the object",
 
171
                                                        UDISKS_TYPE_DAEMON,
 
172
                                                        G_PARAM_READABLE |
 
173
                                                        G_PARAM_WRITABLE |
 
174
                                                        G_PARAM_CONSTRUCT_ONLY |
 
175
                                                        G_PARAM_STATIC_STRINGS));
 
176
}
 
177
 
 
178
/**
 
179
 * udisks_linux_manager_new:
 
180
 * @daemon: A #UDisksDaemon.
 
181
 *
 
182
 * Creates a new #UDisksLinuxManager instance.
 
183
 *
 
184
 * Returns: A new #UDisksLinuxManager. Free with g_object_unref().
 
185
 */
 
186
UDisksManager *
 
187
udisks_linux_manager_new (UDisksDaemon *daemon)
 
188
{
 
189
  g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL);
 
190
  return UDISKS_MANAGER (g_object_new (UDISKS_TYPE_LINUX_MANAGER,
 
191
                                       "daemon", daemon,
 
192
                                       "version", PACKAGE_VERSION,
 
193
                                       NULL));
 
194
}
 
195
 
 
196
/**
 
197
 * udisks_linux_manager_get_daemon:
 
198
 * @manager: A #UDisksLinuxManager.
 
199
 *
 
200
 * Gets the daemon used by @manager.
 
201
 *
 
202
 * Returns: A #UDisksDaemon. Do not free, the object is owned by @manager.
 
203
 */
 
204
UDisksDaemon *
 
205
udisks_linux_manager_get_daemon (UDisksLinuxManager *manager)
 
206
{
 
207
  g_return_val_if_fail (UDISKS_IS_LINUX_MANAGER (manager), NULL);
 
208
  return manager->daemon;
 
209
}
 
210
 
 
211
/* ---------------------------------------------------------------------------------------------------- */
 
212
 
 
213
typedef struct
 
214
{
 
215
  const gchar *loop_device;
 
216
  const gchar *path;
 
217
} WaitForLoopData;
 
218
 
 
219
static UDisksObject *
 
220
wait_for_loop_object (UDisksDaemon *daemon,
 
221
                      gpointer      user_data)
 
222
{
 
223
  WaitForLoopData *data = user_data;
 
224
  UDisksObject *ret = NULL;
 
225
  UDisksObject *object = NULL;
 
226
  UDisksBlock *block;
 
227
  UDisksLoop *loop;
 
228
  UDisksLinuxDevice *device = NULL;
 
229
  GDir *dir;
 
230
 
 
231
  /* First see if we have the right loop object */
 
232
  object = udisks_daemon_find_block_by_device_file (daemon, data->loop_device);
 
233
  if (object == NULL)
 
234
    goto out;
 
235
  block = udisks_object_peek_block (object);
 
236
  loop = udisks_object_peek_loop (object);
 
237
  if (block == NULL || loop == NULL)
 
238
    goto out;
 
239
  if (g_strcmp0 (udisks_loop_get_backing_file (loop), data->path) != 0)
 
240
    goto out;
 
241
 
 
242
  /* We also need to wait for all partitions to be in place in case
 
243
   * the loop device is partitioned... we can do it like this because
 
244
   * we are guaranteed that partitions are in sysfs when receiving the
 
245
   * uevent for the main block device...
 
246
   */
 
247
  device = udisks_linux_block_object_get_device (UDISKS_LINUX_BLOCK_OBJECT (object));
 
248
  if (device == NULL)
 
249
    goto out;
 
250
 
 
251
  dir = g_dir_open (g_udev_device_get_sysfs_path (device->udev_device), 0 /* flags */, NULL /* GError */);
 
252
  if (dir != NULL)
 
253
    {
 
254
      const gchar *name;
 
255
      const gchar *device_name;
 
256
      device_name = g_udev_device_get_name (device->udev_device);
 
257
      while ((name = g_dir_read_name (dir)) != NULL)
 
258
        {
 
259
          if (g_str_has_prefix (name, device_name))
 
260
            {
 
261
              gchar *sysfs_path;
 
262
              UDisksObject *partition_object;
 
263
              sysfs_path = g_strconcat (g_udev_device_get_sysfs_path (device->udev_device), "/", name, NULL);
 
264
              partition_object = udisks_daemon_find_block_by_sysfs_path (daemon, sysfs_path);
 
265
              if (partition_object == NULL)
 
266
                {
 
267
                  /* nope, not there, bail */
 
268
                  g_free (sysfs_path);
 
269
                  g_dir_close (dir);
 
270
                  goto out;
 
271
                }
 
272
              g_object_unref (partition_object);
 
273
              g_free (sysfs_path);
 
274
            }
 
275
        }
 
276
      g_dir_close (dir);
 
277
    }
 
278
 
 
279
  /* all, good return the loop object */
 
280
  ret = g_object_ref (object);
 
281
 
 
282
 out:
 
283
  g_clear_object (&object);
 
284
  g_clear_object (&device);
 
285
  return ret;
 
286
}
 
287
 
 
288
/* ---------------------------------------------------------------------------------------------------- */
 
289
 
 
290
/* runs in thread dedicated to handling @invocation */
 
291
static gboolean
 
292
handle_loop_setup (UDisksManager          *object,
 
293
                   GDBusMethodInvocation  *invocation,
 
294
                   GUnixFDList            *fd_list,
 
295
                   GVariant               *fd_index,
 
296
                   GVariant               *options)
 
297
{
 
298
  UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
 
299
  GError *error;
 
300
  gint fd_num;
 
301
  gint fd = -1;
 
302
  gchar proc_path[64];
 
303
  gchar path[8192];
 
304
  ssize_t path_len;
 
305
  gint loop_fd = -1;
 
306
  gint loop_control_fd = -1;
 
307
  gint allocated_loop_number = -1;
 
308
  gchar *loop_device = NULL;
 
309
  struct loop_info64 li64;
 
310
  UDisksObject *loop_object = NULL;
 
311
  gboolean option_read_only = FALSE;
 
312
  gboolean option_no_part_scan = FALSE;
 
313
  guint64 option_offset = 0;
 
314
  guint64 option_size = 0;
 
315
  uid_t caller_uid;
 
316
  struct stat fd_statbuf;
 
317
  gboolean fd_statbuf_valid = FALSE;
 
318
  WaitForLoopData wait_data;
 
319
 
 
320
  /* we need the uid of the caller for the loop file */
 
321
  error = NULL;
 
322
  if (!udisks_daemon_util_get_caller_uid_sync (manager->daemon, invocation, NULL /* GCancellable */, &caller_uid, NULL, NULL, &error))
 
323
    {
 
324
      g_dbus_method_invocation_return_gerror (invocation, error);
 
325
      g_error_free (error);
 
326
      goto out;
 
327
    }
 
328
 
 
329
  /* Check if the user is authorized to create a loop device */
 
330
  if (!udisks_daemon_util_check_authorization_sync (manager->daemon,
 
331
                                                    NULL,
 
332
                                                    "org.freedesktop.udisks2.loop-setup",
 
333
                                                    options,
 
334
                                                    /* Translators: Shown in authentication dialog when the user
 
335
                                                     * requests setting up a loop device.
 
336
                                                     */
 
337
                                                    N_("Authentication is required to set up a loop device"),
 
338
                                                    invocation))
 
339
    goto out;
 
340
 
 
341
  fd_num = g_variant_get_handle (fd_index);
 
342
  if (fd_list == NULL || fd_num >= g_unix_fd_list_get_length (fd_list))
 
343
    {
 
344
      g_dbus_method_invocation_return_error (invocation,
 
345
                                             UDISKS_ERROR,
 
346
                                             UDISKS_ERROR_FAILED,
 
347
                                             "Expected to use fd at index %d, but message has only %d fds",
 
348
                                             fd_num,
 
349
                                             fd_list == NULL ? 0 : g_unix_fd_list_get_length (fd_list));
 
350
      goto out;
 
351
    }
 
352
  error = NULL;
 
353
  fd = g_unix_fd_list_get (fd_list, fd_num, &error);
 
354
  if (fd == -1)
 
355
    {
 
356
      g_prefix_error (&error, "Error getting file descriptor %d from message: ", fd_num);
 
357
      g_dbus_method_invocation_take_error (invocation, error);
 
358
      goto out;
 
359
    }
 
360
 
 
361
  snprintf (proc_path, sizeof (proc_path), "/proc/%d/fd/%d", getpid (), fd);
 
362
  path_len = readlink (proc_path, path, sizeof (path) - 1);
 
363
  if (path_len < 1)
 
364
    {
 
365
      g_dbus_method_invocation_return_error (invocation,
 
366
                                             UDISKS_ERROR,
 
367
                                             UDISKS_ERROR_FAILED,
 
368
                                             "Error determing path: %m");
 
369
      goto out;
 
370
    }
 
371
  path[path_len] = '\0';
 
372
 
 
373
  g_variant_lookup (options, "read-only", "b", &option_read_only);
 
374
  g_variant_lookup (options, "offset", "t", &option_offset);
 
375
  g_variant_lookup (options, "size", "t", &option_size);
 
376
  g_variant_lookup (options, "no-part-scan", "b", &option_no_part_scan);
 
377
 
 
378
  /* it's not a problem if fstat fails... for example, this can happen if the user
 
379
   * passes a fd to a file on the GVfs fuse mount
 
380
   */
 
381
  if (fstat (fd, &fd_statbuf) == 0)
 
382
    fd_statbuf_valid = TRUE;
 
383
 
 
384
  /* serialize access to /dev/loop-control */
 
385
  g_mutex_lock (&(manager->lock));
 
386
 
 
387
  loop_control_fd = open ("/dev/loop-control", O_RDWR);
 
388
  if (loop_control_fd == -1)
 
389
    {
 
390
      g_dbus_method_invocation_return_error (invocation,
 
391
                                             UDISKS_ERROR,
 
392
                                             UDISKS_ERROR_FAILED,
 
393
                                             "Error opening /dev/loop-control: %m");
 
394
      g_mutex_unlock (&(manager->lock));
 
395
      goto out;
 
396
    }
 
397
 
 
398
  allocated_loop_number = ioctl (loop_control_fd, LOOP_CTL_GET_FREE);
 
399
  if (allocated_loop_number < 0)
 
400
    {
 
401
      g_dbus_method_invocation_return_error (invocation,
 
402
                                             UDISKS_ERROR,
 
403
                                             UDISKS_ERROR_FAILED,
 
404
                                             "Error allocating free loop device: %m");
 
405
      g_mutex_unlock (&(manager->lock));
 
406
      goto out;
 
407
    }
 
408
 
 
409
  loop_device = g_strdup_printf ("/dev/loop%d", allocated_loop_number);
 
410
  loop_fd = open (loop_device, option_read_only ? O_RDONLY : O_RDWR);
 
411
  if (loop_fd == -1)
 
412
    {
 
413
      g_dbus_method_invocation_return_error (invocation,
 
414
                                             UDISKS_ERROR,
 
415
                                             UDISKS_ERROR_FAILED,
 
416
                                             "Cannot open %s: %m", loop_device);
 
417
      g_mutex_unlock (&(manager->lock));
 
418
      goto out;
 
419
    }
 
420
 
 
421
  /* update the loop file - need to do this before getting the uevent for the device  */
 
422
  udisks_state_add_loop (udisks_daemon_get_state (manager->daemon),
 
423
                         loop_device,
 
424
                         path,
 
425
                         fd_statbuf_valid ? fd_statbuf.st_dev : 0,
 
426
                         caller_uid);
 
427
 
 
428
  memset (&li64, '\0', sizeof (li64));
 
429
  strncpy ((char *) li64.lo_file_name, path, LO_NAME_SIZE - 1);
 
430
  if (option_read_only)
 
431
    li64.lo_flags |= LO_FLAGS_READ_ONLY;
 
432
  if (!option_no_part_scan)
 
433
    li64.lo_flags |= 8; /* Use LO_FLAGS_PARTSCAN when 3.2 has been out for a while */
 
434
  li64.lo_offset = option_offset;
 
435
  li64.lo_sizelimit = option_size;
 
436
  if (ioctl (loop_fd, LOOP_SET_FD, fd) < 0 || ioctl (loop_fd, LOOP_SET_STATUS64, &li64) < 0)
 
437
    {
 
438
      g_dbus_method_invocation_return_error (invocation,
 
439
                                             UDISKS_ERROR,
 
440
                                             UDISKS_ERROR_FAILED,
 
441
                                             "Error setting up loop device %s: %m",
 
442
                                             loop_device);
 
443
      g_mutex_unlock (&(manager->lock));
 
444
      goto out;
 
445
    }
 
446
  g_mutex_unlock (&(manager->lock));
 
447
 
 
448
  /* Determine the resulting object */
 
449
  error = NULL;
 
450
  wait_data.loop_device = loop_device;
 
451
  wait_data.path = path;
 
452
  loop_object = udisks_daemon_wait_for_object_sync (manager->daemon,
 
453
                                                    wait_for_loop_object,
 
454
                                                    &wait_data,
 
455
                                                    NULL,
 
456
                                                    10, /* timeout_seconds */
 
457
                                                    &error);
 
458
  if (loop_object == NULL)
 
459
    {
 
460
      g_prefix_error (&error,
 
461
                      "Error waiting for loop object after creating %s",
 
462
                      loop_device);
 
463
      g_dbus_method_invocation_take_error (invocation, error);
 
464
      goto out;
 
465
    }
 
466
 
 
467
  udisks_notice ("Set up loop device %s (backed by %s)",
 
468
                 loop_device,
 
469
                 path);
 
470
 
 
471
  udisks_manager_complete_loop_setup (object,
 
472
                                      invocation,
 
473
                                      NULL, /* fd_list */
 
474
                                      g_dbus_object_get_object_path (G_DBUS_OBJECT (loop_object)));
 
475
 
 
476
 out:
 
477
  if (loop_object != NULL)
 
478
    g_object_unref (loop_object);
 
479
  g_free (loop_device);
 
480
  if (loop_control_fd != -1)
 
481
    close (loop_control_fd);
 
482
  if (loop_fd != -1)
 
483
    close (loop_fd);
 
484
  if (fd != -1)
 
485
    close (fd);
 
486
  return TRUE; /* returning TRUE means that we handled the method invocation */
 
487
}
 
488
 
 
489
/* ---------------------------------------------------------------------------------------------------- */
 
490
 
 
491
typedef struct
 
492
{
 
493
  gint md_num;
 
494
} WaitForArrayData;
 
495
 
 
496
static UDisksObject *
 
497
wait_for_array_object (UDisksDaemon *daemon,
 
498
                       gpointer      user_data)
 
499
{
 
500
  const gchar *raid_device_file = user_data;
 
501
  UDisksObject *object = NULL;
 
502
  UDisksBlock *block = NULL;
 
503
  gchar *mdraid_objpath = NULL;
 
504
  UDisksObject *ret = NULL;
 
505
 
 
506
  /* First see if we have the right array object */
 
507
  object = udisks_daemon_find_block_by_device_file (daemon, raid_device_file);
 
508
  if (object == NULL)
 
509
    goto out;
 
510
 
 
511
  block = udisks_object_get_block (object);
 
512
  if (block == NULL)
 
513
    goto out;
 
514
 
 
515
  mdraid_objpath = udisks_block_dup_mdraid (block);
 
516
  if (g_strcmp0 (mdraid_objpath, "/") == 0)
 
517
    goto out;
 
518
 
 
519
  ret = udisks_daemon_find_object (daemon, mdraid_objpath);
 
520
 
 
521
 out:
 
522
  g_free (mdraid_objpath);
 
523
  g_clear_object (&block);
 
524
  g_clear_object (&object);
 
525
  return ret;
 
526
}
 
527
 
 
528
static const gchar *raid_level_whitelist[] = {"raid0", "raid1", "raid4", "raid5", "raid6", "raid10", NULL};
 
529
 
 
530
static gboolean
 
531
handle_mdraid_create (UDisksManager         *_object,
 
532
                      GDBusMethodInvocation *invocation,
 
533
                      const gchar *const    *arg_blocks,
 
534
                      const gchar           *arg_level,
 
535
                      const gchar           *arg_name,
 
536
                      guint64                arg_chunk,
 
537
                      GVariant              *arg_options)
 
538
{
 
539
  UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (_object);
 
540
  UDisksObject *array_object = NULL;
 
541
  WaitForArrayData wait_data;
 
542
  uid_t caller_uid;
 
543
  GError *error = NULL;
 
544
  const gchar *message;
 
545
  const gchar *action_id;
 
546
  guint num_devices = 0;
 
547
  GList *blocks = NULL;
 
548
  GList *l;
 
549
  guint n;
 
550
  gchar *escaped_name = NULL;
 
551
  GString *str = NULL;
 
552
  gint status;
 
553
  gchar *error_message = NULL;
 
554
  gchar *raid_device_file = NULL;
 
555
  struct stat statbuf;
 
556
  dev_t raid_device_num;
 
557
 
 
558
  error = NULL;
 
559
  if (!udisks_daemon_util_get_caller_uid_sync (manager->daemon, invocation, NULL /* GCancellable */, &caller_uid, NULL, NULL, &error))
 
560
    {
 
561
      g_dbus_method_invocation_return_gerror (invocation, error);
 
562
      g_clear_error (&error);
 
563
      goto out;
 
564
    }
 
565
 
 
566
  /* Translators: Shown in authentication dialog when the user
 
567
   * attempts to start a RAID Array.
 
568
   */
 
569
  /* TODO: variables */
 
570
  message = N_("Authentication is required to create a RAID array");
 
571
  action_id = "org.freedesktop.udisks2.manage-md-raid";
 
572
  if (!udisks_daemon_util_check_authorization_sync (manager->daemon,
 
573
                                                    NULL,
 
574
                                                    action_id,
 
575
                                                    arg_options,
 
576
                                                    message,
 
577
                                                    invocation))
 
578
    goto out;
 
579
 
 
580
  /* validate level */
 
581
  for (n = 0; raid_level_whitelist[n] != NULL; n++)
 
582
    {
 
583
      if (g_strcmp0 (raid_level_whitelist[n], arg_level) == 0)
 
584
        break;
 
585
    }
 
586
  if (raid_level_whitelist[n] == NULL)
 
587
    {
 
588
      g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
 
589
                                             "Unsupported RAID level %s", arg_level);
 
590
      goto out;
 
591
    }
 
592
 
 
593
  /* validate chunk (TODO: check that it's a power of 2) */
 
594
  if ((arg_chunk & 0x0fff) != 0)
 
595
    {
 
596
      g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
 
597
                                             "Chunk %" G_GUINT64_FORMAT " is not a multiple of 4KiB", arg_chunk);
 
598
      goto out;
 
599
    }
 
600
 
 
601
  /* validate name */
 
602
  if (g_strcmp0 (arg_level, "raid1") == 0 && arg_chunk != 0)
 
603
    {
 
604
      g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
 
605
                                             "Chunk must be zero for level 'raid1'");
 
606
      goto out;
 
607
    }
 
608
 
 
609
  /* validate name */
 
610
  if (strlen (arg_name) > 32)
 
611
    {
 
612
      g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
 
613
                                             "Name is invalid");
 
614
      goto out;
 
615
    }
 
616
 
 
617
  num_devices = g_strv_length ((gchar **) arg_blocks);
 
618
 
 
619
  /* validate number of devices */
 
620
  if (num_devices < 2)
 
621
    {
 
622
      g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
 
623
                                             "Must have at least two devices");
 
624
      goto out;
 
625
    }
 
626
 
 
627
  /* Collect and validate block objects
 
628
   *
 
629
   * Also, check we can open the block devices at the same time - this
 
630
   * is to avoid start deleting half the block devices while the other
 
631
   * half is already in use.
 
632
   */
 
633
  for (n = 0; arg_blocks != NULL && arg_blocks[n] != NULL; n++)
 
634
    {
 
635
      UDisksObject *object = NULL;
 
636
      UDisksBlock *block = NULL;
 
637
      gchar *device_file = NULL;
 
638
      int fd;
 
639
 
 
640
      object = udisks_daemon_find_object (manager->daemon, arg_blocks[n]);
 
641
      if (object == NULL)
 
642
        {
 
643
          g_dbus_method_invocation_return_error (invocation,
 
644
                                                 UDISKS_ERROR,
 
645
                                                 UDISKS_ERROR_FAILED,
 
646
                                                 "Invalid object path %s at index %d",
 
647
                                                 arg_blocks[n], n);
 
648
          goto out;
 
649
        }
 
650
 
 
651
      block = udisks_object_get_block (object);
 
652
      if (block == NULL)
 
653
        {
 
654
          g_dbus_method_invocation_return_error (invocation,
 
655
                                                 UDISKS_ERROR,
 
656
                                                 UDISKS_ERROR_FAILED,
 
657
                                                 "Object path %s for index %d is not a block device",
 
658
                                                 arg_blocks[n], n);
 
659
          goto out;
 
660
        }
 
661
 
 
662
      device_file = udisks_block_dup_device (block);
 
663
      fd = open (device_file, O_RDWR | O_EXCL);
 
664
      if (fd < 0)
 
665
        {
 
666
          g_dbus_method_invocation_return_error (invocation,
 
667
                                                 UDISKS_ERROR,
 
668
                                                 UDISKS_ERROR_FAILED,
 
669
                                                 "Error opening device %s: %m",
 
670
                                                 device_file);
 
671
          g_free (device_file);
 
672
          goto out;
 
673
        }
 
674
      close (fd);
 
675
      g_free (device_file);
 
676
 
 
677
      blocks = g_list_prepend (blocks, block); /* adopts ownership */
 
678
      g_object_unref (object);
 
679
    }
 
680
  blocks = g_list_reverse (blocks);
 
681
 
 
682
  /* wipe existing devices */
 
683
  for (l = blocks; l != NULL; l = l->next)
 
684
    {
 
685
      UDisksBlock *block = UDISKS_BLOCK (l->data);
 
686
      UDisksObject *object_for_block;
 
687
      gchar *escaped_device;
 
688
      object_for_block = udisks_daemon_util_dup_object (block, &error);
 
689
      if (object_for_block == NULL)
 
690
        {
 
691
          g_dbus_method_invocation_return_gerror (invocation, error);
 
692
          g_clear_error (&error);
 
693
          goto out;
 
694
        }
 
695
      escaped_device = udisks_daemon_util_escape (udisks_block_get_device (block));
 
696
      if (!udisks_daemon_launch_spawned_job_sync (manager->daemon,
 
697
                                                  object_for_block,
 
698
                                                  "format-erase", caller_uid,
 
699
                                                  NULL, /* cancellable */
 
700
                                                  0,    /* uid_t run_as_uid */
 
701
                                                  0,    /* uid_t run_as_euid */
 
702
                                                  &status,
 
703
                                                  &error_message,
 
704
                                                  NULL, /* input_string */
 
705
                                                  "wipefs -a \"%s\"",
 
706
                                                  escaped_device))
 
707
        {
 
708
          g_dbus_method_invocation_return_error (invocation,
 
709
                                                 UDISKS_ERROR,
 
710
                                                 UDISKS_ERROR_FAILED,
 
711
                                                 "Error wiping device %s to be used in a RAID array: %s",
 
712
                                                 udisks_block_get_device (block),
 
713
                                                 error_message);
 
714
          g_free (error_message);
 
715
          g_object_unref (object_for_block);
 
716
          g_free (escaped_device);
 
717
          goto out;
 
718
        }
 
719
      g_object_unref (object_for_block);
 
720
      g_free (escaped_device);
 
721
    }
 
722
 
 
723
  /* Create the array... */
 
724
  escaped_name = udisks_daemon_util_escape (arg_name);
 
725
  str = g_string_new ("mdadm");
 
726
  raid_device_file = udisks_daemon_util_get_free_mdraid_device ();
 
727
  if (raid_device_file == NULL)
 
728
    {
 
729
      g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
 
730
                                             "Unable to find free MD device");
 
731
      goto out;
 
732
    }
 
733
  g_string_append_printf (str, " --create %s", raid_device_file);
 
734
  g_string_append_printf (str, " --run");
 
735
  if (arg_chunk > 0)
 
736
    g_string_append_printf (str, " --chunk %" G_GUINT64_FORMAT, (guint64) (arg_chunk / 1024LL));
 
737
  g_string_append_printf (str, " --level %s", arg_level);
 
738
  if (strlen (arg_name) > 0)
 
739
    g_string_append_printf (str, " --name \"%s\"", escaped_name);
 
740
  g_string_append_printf (str, " --raid-devices %d", num_devices);
 
741
  for (l = blocks; l != NULL; l = l->next)
 
742
    {
 
743
      UDisksBlock *block = UDISKS_BLOCK (l->data);
 
744
      gchar *escaped_device;
 
745
      escaped_device = udisks_daemon_util_escape (udisks_block_get_device (block));
 
746
      g_string_append_printf (str, " \"%s\"", escaped_device);
 
747
      g_free (escaped_device);
 
748
    }
 
749
 
 
750
  if (!udisks_daemon_launch_spawned_job_sync (manager->daemon,
 
751
                                              NULL,
 
752
                                              "mdraid-create", caller_uid,
 
753
                                              NULL, /* cancellable */
 
754
                                              0,    /* uid_t run_as_uid */
 
755
                                              0,    /* uid_t run_as_euid */
 
756
                                              &status,
 
757
                                              &error_message,
 
758
                                              NULL, /* input_string */
 
759
                                              "%s",
 
760
                                              str->str))
 
761
    {
 
762
      g_dbus_method_invocation_return_error (invocation,
 
763
                                             UDISKS_ERROR,
 
764
                                             UDISKS_ERROR_FAILED,
 
765
                                             "Error creating RAID array: %s",
 
766
                                             error_message);
 
767
      g_free (error_message);
 
768
      goto out;
 
769
    }
 
770
 
 
771
  /* ... then, sit and wait for raid array object to show up */
 
772
  array_object = udisks_daemon_wait_for_object_sync (manager->daemon,
 
773
                                                     wait_for_array_object,
 
774
                                                     raid_device_file,
 
775
                                                     NULL,
 
776
                                                     10, /* timeout_seconds */
 
777
                                                     &error);
 
778
  if (array_object == NULL)
 
779
    {
 
780
      g_prefix_error (&error,
 
781
                      "Error waiting for array object after creating /dev/md%d",
 
782
                      wait_data.md_num);
 
783
      g_dbus_method_invocation_take_error (invocation, error);
 
784
      goto out;
 
785
    }
 
786
 
 
787
  if (stat (raid_device_file, &statbuf) != 0)
 
788
    {
 
789
      g_dbus_method_invocation_return_error (invocation,
 
790
                                             UDISKS_ERROR,
 
791
                                             UDISKS_ERROR_FAILED,
 
792
                                             "Error calling stat(2) on %s: %m",
 
793
                                             raid_device_file);
 
794
      goto out;
 
795
    }
 
796
  if (!S_ISBLK (statbuf.st_mode))
 
797
    {
 
798
      g_dbus_method_invocation_return_error (invocation,
 
799
                                             UDISKS_ERROR,
 
800
                                             UDISKS_ERROR_FAILED,
 
801
                                             "Device file %s is not a block device",
 
802
                                             raid_device_file);
 
803
      goto out;
 
804
    }
 
805
  raid_device_num = statbuf.st_rdev;
 
806
 
 
807
  /* update the mdraid file */
 
808
  udisks_state_add_mdraid (udisks_daemon_get_state (manager->daemon),
 
809
                           raid_device_num,
 
810
                           caller_uid);
 
811
 
 
812
  /* ... wipe the created RAID array */
 
813
  if (!udisks_daemon_launch_spawned_job_sync (manager->daemon,
 
814
                                              array_object,
 
815
                                              "format-erase", caller_uid,
 
816
                                              NULL, /* cancellable */
 
817
                                              0,    /* uid_t run_as_uid */
 
818
                                              0,    /* uid_t run_as_euid */
 
819
                                              &status,
 
820
                                              &error_message,
 
821
                                              NULL, /* input_string */
 
822
                                              "wipefs -a %s",
 
823
                                              raid_device_file))
 
824
    {
 
825
      g_dbus_method_invocation_return_error (invocation,
 
826
                                             UDISKS_ERROR,
 
827
                                             UDISKS_ERROR_FAILED,
 
828
                                             "Error wiping raid device %s: %s",
 
829
                                             raid_device_file,
 
830
                                             error_message);
 
831
      goto out;
 
832
    }
 
833
 
 
834
  /* ... finally trigger uevents on the members - we want this so the
 
835
   * udev database is updated for them with e.g. ID_FS_TYPE. Ideally
 
836
   * mdadm(8) or whatever thing is writing out the RAID metadata would
 
837
   * ensure this, but that's not how things currently work :-/
 
838
   */
 
839
  for (l = blocks; l != NULL; l = l->next)
 
840
    {
 
841
      UDisksBlock *block = UDISKS_BLOCK (l->data);
 
842
      UDisksObject *object_for_block;
 
843
      object_for_block = udisks_daemon_util_dup_object (block, &error);
 
844
      if (object_for_block == NULL)
 
845
        {
 
846
          g_dbus_method_invocation_return_gerror (invocation, error);
 
847
          g_clear_error (&error);
 
848
          goto out;
 
849
        }
 
850
      udisks_linux_block_object_trigger_uevent (UDISKS_LINUX_BLOCK_OBJECT (object_for_block));
 
851
      g_object_unref (object_for_block);
 
852
    }
 
853
 
 
854
  /* ... and, we're done! */
 
855
  udisks_manager_complete_mdraid_create (_object,
 
856
                                         invocation,
 
857
                                         g_dbus_object_get_object_path (G_DBUS_OBJECT (array_object)));
 
858
 
 
859
 out:
 
860
  g_free (raid_device_file);
 
861
  if (str != NULL)
 
862
    g_string_free (str, TRUE);
 
863
  g_list_free_full (blocks, g_object_unref);
 
864
  g_free (escaped_name);
 
865
  g_clear_object (&array_object);
 
866
 
 
867
  return TRUE; /* returning TRUE means that we handled the method invocation */
 
868
}
 
869
 
 
870
/* ---------------------------------------------------------------------------------------------------- */
 
871
 
 
872
static void
 
873
manager_iface_init (UDisksManagerIface *iface)
 
874
{
 
875
  iface->handle_loop_setup = handle_loop_setup;
 
876
  iface->handle_mdraid_create = handle_mdraid_create;
 
877
}