1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
3
* Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
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.
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.
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
22
#include <glib/gi18n-lib.h>
23
#include <gio/gunixfdlist.h>
24
#include <glib/gstdio.h>
26
#include <sys/types.h>
29
#include <sys/ioctl.h>
37
#include <linux/loop.h>
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"
48
* SECTION:udiskslinuxmanager
49
* @title: UDisksLinuxManager
50
* @short_description: Linux implementation of #UDisksManager
52
* This type provides an implementation of the #UDisksManager
56
typedef struct _UDisksLinuxManagerClass UDisksLinuxManagerClass;
61
* The #UDisksLinuxManager structure contains only private data and should
62
* only be accessed using the provided API.
64
struct _UDisksLinuxManager
66
UDisksManagerSkeleton parent_instance;
73
struct _UDisksLinuxManagerClass
75
UDisksManagerSkeletonClass parent_class;
84
static void manager_iface_init (UDisksManagerIface *iface);
86
G_DEFINE_TYPE_WITH_CODE (UDisksLinuxManager, udisks_linux_manager, UDISKS_TYPE_MANAGER_SKELETON,
87
G_IMPLEMENT_INTERFACE (UDISKS_TYPE_MANAGER, manager_iface_init));
89
/* ---------------------------------------------------------------------------------------------------- */
92
udisks_linux_manager_finalize (GObject *object)
94
UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
96
g_mutex_clear (&(manager->lock));
98
G_OBJECT_CLASS (udisks_linux_manager_parent_class)->finalize (object);
102
udisks_linux_manager_get_property (GObject *object,
107
UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
112
g_value_set_object (value, udisks_linux_manager_get_daemon (manager));
116
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
122
udisks_linux_manager_set_property (GObject *object,
127
UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
132
g_assert (manager->daemon == NULL);
133
/* we don't take a reference to the daemon */
134
manager->daemon = g_value_get_object (value);
138
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
144
udisks_linux_manager_init (UDisksLinuxManager *manager)
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);
152
udisks_linux_manager_class_init (UDisksLinuxManagerClass *klass)
154
GObjectClass *gobject_class;
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;
162
* UDisksLinuxManager:daemon:
164
* The #UDisksDaemon for the object.
166
g_object_class_install_property (gobject_class,
168
g_param_spec_object ("daemon",
170
"The daemon for the object",
174
G_PARAM_CONSTRUCT_ONLY |
175
G_PARAM_STATIC_STRINGS));
179
* udisks_linux_manager_new:
180
* @daemon: A #UDisksDaemon.
182
* Creates a new #UDisksLinuxManager instance.
184
* Returns: A new #UDisksLinuxManager. Free with g_object_unref().
187
udisks_linux_manager_new (UDisksDaemon *daemon)
189
g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL);
190
return UDISKS_MANAGER (g_object_new (UDISKS_TYPE_LINUX_MANAGER,
192
"version", PACKAGE_VERSION,
197
* udisks_linux_manager_get_daemon:
198
* @manager: A #UDisksLinuxManager.
200
* Gets the daemon used by @manager.
202
* Returns: A #UDisksDaemon. Do not free, the object is owned by @manager.
205
udisks_linux_manager_get_daemon (UDisksLinuxManager *manager)
207
g_return_val_if_fail (UDISKS_IS_LINUX_MANAGER (manager), NULL);
208
return manager->daemon;
211
/* ---------------------------------------------------------------------------------------------------- */
215
const gchar *loop_device;
219
static UDisksObject *
220
wait_for_loop_object (UDisksDaemon *daemon,
223
WaitForLoopData *data = user_data;
224
UDisksObject *ret = NULL;
225
UDisksObject *object = NULL;
228
UDisksLinuxDevice *device = NULL;
231
/* First see if we have the right loop object */
232
object = udisks_daemon_find_block_by_device_file (daemon, data->loop_device);
235
block = udisks_object_peek_block (object);
236
loop = udisks_object_peek_loop (object);
237
if (block == NULL || loop == NULL)
239
if (g_strcmp0 (udisks_loop_get_backing_file (loop), data->path) != 0)
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...
247
device = udisks_linux_block_object_get_device (UDISKS_LINUX_BLOCK_OBJECT (object));
251
dir = g_dir_open (g_udev_device_get_sysfs_path (device->udev_device), 0 /* flags */, NULL /* GError */);
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)
259
if (g_str_has_prefix (name, device_name))
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)
267
/* nope, not there, bail */
272
g_object_unref (partition_object);
279
/* all, good return the loop object */
280
ret = g_object_ref (object);
283
g_clear_object (&object);
284
g_clear_object (&device);
288
/* ---------------------------------------------------------------------------------------------------- */
290
/* runs in thread dedicated to handling @invocation */
292
handle_loop_setup (UDisksManager *object,
293
GDBusMethodInvocation *invocation,
294
GUnixFDList *fd_list,
298
UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (object);
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;
316
struct stat fd_statbuf;
317
gboolean fd_statbuf_valid = FALSE;
318
WaitForLoopData wait_data;
320
/* we need the uid of the caller for the loop file */
322
if (!udisks_daemon_util_get_caller_uid_sync (manager->daemon, invocation, NULL /* GCancellable */, &caller_uid, NULL, NULL, &error))
324
g_dbus_method_invocation_return_gerror (invocation, error);
325
g_error_free (error);
329
/* Check if the user is authorized to create a loop device */
330
if (!udisks_daemon_util_check_authorization_sync (manager->daemon,
332
"org.freedesktop.udisks2.loop-setup",
334
/* Translators: Shown in authentication dialog when the user
335
* requests setting up a loop device.
337
N_("Authentication is required to set up a loop device"),
341
fd_num = g_variant_get_handle (fd_index);
342
if (fd_list == NULL || fd_num >= g_unix_fd_list_get_length (fd_list))
344
g_dbus_method_invocation_return_error (invocation,
347
"Expected to use fd at index %d, but message has only %d fds",
349
fd_list == NULL ? 0 : g_unix_fd_list_get_length (fd_list));
353
fd = g_unix_fd_list_get (fd_list, fd_num, &error);
356
g_prefix_error (&error, "Error getting file descriptor %d from message: ", fd_num);
357
g_dbus_method_invocation_take_error (invocation, error);
361
snprintf (proc_path, sizeof (proc_path), "/proc/%d/fd/%d", getpid (), fd);
362
path_len = readlink (proc_path, path, sizeof (path) - 1);
365
g_dbus_method_invocation_return_error (invocation,
368
"Error determing path: %m");
371
path[path_len] = '\0';
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);
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
381
if (fstat (fd, &fd_statbuf) == 0)
382
fd_statbuf_valid = TRUE;
384
/* serialize access to /dev/loop-control */
385
g_mutex_lock (&(manager->lock));
387
loop_control_fd = open ("/dev/loop-control", O_RDWR);
388
if (loop_control_fd == -1)
390
g_dbus_method_invocation_return_error (invocation,
393
"Error opening /dev/loop-control: %m");
394
g_mutex_unlock (&(manager->lock));
398
allocated_loop_number = ioctl (loop_control_fd, LOOP_CTL_GET_FREE);
399
if (allocated_loop_number < 0)
401
g_dbus_method_invocation_return_error (invocation,
404
"Error allocating free loop device: %m");
405
g_mutex_unlock (&(manager->lock));
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);
413
g_dbus_method_invocation_return_error (invocation,
416
"Cannot open %s: %m", loop_device);
417
g_mutex_unlock (&(manager->lock));
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),
425
fd_statbuf_valid ? fd_statbuf.st_dev : 0,
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)
438
g_dbus_method_invocation_return_error (invocation,
441
"Error setting up loop device %s: %m",
443
g_mutex_unlock (&(manager->lock));
446
g_mutex_unlock (&(manager->lock));
448
/* Determine the resulting object */
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,
456
10, /* timeout_seconds */
458
if (loop_object == NULL)
460
g_prefix_error (&error,
461
"Error waiting for loop object after creating %s",
463
g_dbus_method_invocation_take_error (invocation, error);
467
udisks_notice ("Set up loop device %s (backed by %s)",
471
udisks_manager_complete_loop_setup (object,
474
g_dbus_object_get_object_path (G_DBUS_OBJECT (loop_object)));
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);
486
return TRUE; /* returning TRUE means that we handled the method invocation */
489
/* ---------------------------------------------------------------------------------------------------- */
496
static UDisksObject *
497
wait_for_array_object (UDisksDaemon *daemon,
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;
506
/* First see if we have the right array object */
507
object = udisks_daemon_find_block_by_device_file (daemon, raid_device_file);
511
block = udisks_object_get_block (object);
515
mdraid_objpath = udisks_block_dup_mdraid (block);
516
if (g_strcmp0 (mdraid_objpath, "/") == 0)
519
ret = udisks_daemon_find_object (daemon, mdraid_objpath);
522
g_free (mdraid_objpath);
523
g_clear_object (&block);
524
g_clear_object (&object);
528
static const gchar *raid_level_whitelist[] = {"raid0", "raid1", "raid4", "raid5", "raid6", "raid10", NULL};
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,
537
GVariant *arg_options)
539
UDisksLinuxManager *manager = UDISKS_LINUX_MANAGER (_object);
540
UDisksObject *array_object = NULL;
541
WaitForArrayData wait_data;
543
GError *error = NULL;
544
const gchar *message;
545
const gchar *action_id;
546
guint num_devices = 0;
547
GList *blocks = NULL;
550
gchar *escaped_name = NULL;
553
gchar *error_message = NULL;
554
gchar *raid_device_file = NULL;
556
dev_t raid_device_num;
559
if (!udisks_daemon_util_get_caller_uid_sync (manager->daemon, invocation, NULL /* GCancellable */, &caller_uid, NULL, NULL, &error))
561
g_dbus_method_invocation_return_gerror (invocation, error);
562
g_clear_error (&error);
566
/* Translators: Shown in authentication dialog when the user
567
* attempts to start a RAID Array.
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,
581
for (n = 0; raid_level_whitelist[n] != NULL; n++)
583
if (g_strcmp0 (raid_level_whitelist[n], arg_level) == 0)
586
if (raid_level_whitelist[n] == NULL)
588
g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
589
"Unsupported RAID level %s", arg_level);
593
/* validate chunk (TODO: check that it's a power of 2) */
594
if ((arg_chunk & 0x0fff) != 0)
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);
602
if (g_strcmp0 (arg_level, "raid1") == 0 && arg_chunk != 0)
604
g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
605
"Chunk must be zero for level 'raid1'");
610
if (strlen (arg_name) > 32)
612
g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
617
num_devices = g_strv_length ((gchar **) arg_blocks);
619
/* validate number of devices */
622
g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
623
"Must have at least two devices");
627
/* Collect and validate block objects
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.
633
for (n = 0; arg_blocks != NULL && arg_blocks[n] != NULL; n++)
635
UDisksObject *object = NULL;
636
UDisksBlock *block = NULL;
637
gchar *device_file = NULL;
640
object = udisks_daemon_find_object (manager->daemon, arg_blocks[n]);
643
g_dbus_method_invocation_return_error (invocation,
646
"Invalid object path %s at index %d",
651
block = udisks_object_get_block (object);
654
g_dbus_method_invocation_return_error (invocation,
657
"Object path %s for index %d is not a block device",
662
device_file = udisks_block_dup_device (block);
663
fd = open (device_file, O_RDWR | O_EXCL);
666
g_dbus_method_invocation_return_error (invocation,
669
"Error opening device %s: %m",
671
g_free (device_file);
675
g_free (device_file);
677
blocks = g_list_prepend (blocks, block); /* adopts ownership */
678
g_object_unref (object);
680
blocks = g_list_reverse (blocks);
682
/* wipe existing devices */
683
for (l = blocks; l != NULL; l = l->next)
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)
691
g_dbus_method_invocation_return_gerror (invocation, error);
692
g_clear_error (&error);
695
escaped_device = udisks_daemon_util_escape (udisks_block_get_device (block));
696
if (!udisks_daemon_launch_spawned_job_sync (manager->daemon,
698
"format-erase", caller_uid,
699
NULL, /* cancellable */
700
0, /* uid_t run_as_uid */
701
0, /* uid_t run_as_euid */
704
NULL, /* input_string */
708
g_dbus_method_invocation_return_error (invocation,
711
"Error wiping device %s to be used in a RAID array: %s",
712
udisks_block_get_device (block),
714
g_free (error_message);
715
g_object_unref (object_for_block);
716
g_free (escaped_device);
719
g_object_unref (object_for_block);
720
g_free (escaped_device);
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)
729
g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
730
"Unable to find free MD device");
733
g_string_append_printf (str, " --create %s", raid_device_file);
734
g_string_append_printf (str, " --run");
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)
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);
750
if (!udisks_daemon_launch_spawned_job_sync (manager->daemon,
752
"mdraid-create", caller_uid,
753
NULL, /* cancellable */
754
0, /* uid_t run_as_uid */
755
0, /* uid_t run_as_euid */
758
NULL, /* input_string */
762
g_dbus_method_invocation_return_error (invocation,
765
"Error creating RAID array: %s",
767
g_free (error_message);
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,
776
10, /* timeout_seconds */
778
if (array_object == NULL)
780
g_prefix_error (&error,
781
"Error waiting for array object after creating /dev/md%d",
783
g_dbus_method_invocation_take_error (invocation, error);
787
if (stat (raid_device_file, &statbuf) != 0)
789
g_dbus_method_invocation_return_error (invocation,
792
"Error calling stat(2) on %s: %m",
796
if (!S_ISBLK (statbuf.st_mode))
798
g_dbus_method_invocation_return_error (invocation,
801
"Device file %s is not a block device",
805
raid_device_num = statbuf.st_rdev;
807
/* update the mdraid file */
808
udisks_state_add_mdraid (udisks_daemon_get_state (manager->daemon),
812
/* ... wipe the created RAID array */
813
if (!udisks_daemon_launch_spawned_job_sync (manager->daemon,
815
"format-erase", caller_uid,
816
NULL, /* cancellable */
817
0, /* uid_t run_as_uid */
818
0, /* uid_t run_as_euid */
821
NULL, /* input_string */
825
g_dbus_method_invocation_return_error (invocation,
828
"Error wiping raid device %s: %s",
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 :-/
839
for (l = blocks; l != NULL; l = l->next)
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)
846
g_dbus_method_invocation_return_gerror (invocation, error);
847
g_clear_error (&error);
850
udisks_linux_block_object_trigger_uevent (UDISKS_LINUX_BLOCK_OBJECT (object_for_block));
851
g_object_unref (object_for_block);
854
/* ... and, we're done! */
855
udisks_manager_complete_mdraid_create (_object,
857
g_dbus_object_get_object_path (G_DBUS_OBJECT (array_object)));
860
g_free (raid_device_file);
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);
867
return TRUE; /* returning TRUE means that we handled the method invocation */
870
/* ---------------------------------------------------------------------------------------------------- */
873
manager_iface_init (UDisksManagerIface *iface)
875
iface->handle_loop_setup = handle_loop_setup;
876
iface->handle_mdraid_create = handle_mdraid_create;