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 
24 #include <sys/types.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <sys/file.h>
31 
32 #include <glib/gstdio.h>
33 
34 #include <blockdev/part.h>
35 #include <blockdev/fs.h>
36 
37 #include "udiskslogging.h"
38 #include "udiskslinuxpartitiontable.h"
39 #include "udiskslinuxblockobject.h"
40 #include "udisksdaemon.h"
41 #include "udisksdaemonutil.h"
42 #include "udiskslinuxdevice.h"
43 #include "udiskslinuxblock.h"
44 #include "udiskslinuxpartition.h"
45 #include "udiskssimplejob.h"
46 
47 /**
48  * SECTION:udiskslinuxpartitiontable
49  * @title: UDisksLinuxPartitionTable
50  * @short_description: Linux implementation of #UDisksPartitionTable
51  *
52  * This type provides an implementation of the #UDisksPartitionTable
53  * interface on Linux.
54  */
55 
56 typedef struct _UDisksLinuxPartitionTableClass   UDisksLinuxPartitionTableClass;
57 
58 /**
59  * UDisksLinuxPartitionTable:
60  *
61  * The #UDisksLinuxPartitionTable structure contains only private data and should
62  * only be accessed using the provided API.
63  */
64 struct _UDisksLinuxPartitionTable
65 {
66   UDisksPartitionTableSkeleton parent_instance;
67 };
68 
69 struct _UDisksLinuxPartitionTableClass
70 {
71   UDisksPartitionTableSkeletonClass parent_class;
72 };
73 
74 static void partition_table_iface_init (UDisksPartitionTableIface *iface);
75 
76 G_DEFINE_TYPE_WITH_CODE (UDisksLinuxPartitionTable, udisks_linux_partition_table, UDISKS_TYPE_PARTITION_TABLE_SKELETON,
77                          G_IMPLEMENT_INTERFACE (UDISKS_TYPE_PARTITION_TABLE, partition_table_iface_init));
78 
79 /* ---------------------------------------------------------------------------------------------------- */
80 
81 static void
udisks_linux_partition_table_init(UDisksLinuxPartitionTable * partition_table)82 udisks_linux_partition_table_init (UDisksLinuxPartitionTable *partition_table)
83 {
84   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (partition_table),
85                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
86 }
87 
88 static void
udisks_linux_partition_table_class_init(UDisksLinuxPartitionTableClass * klass)89 udisks_linux_partition_table_class_init (UDisksLinuxPartitionTableClass *klass)
90 {
91 }
92 
93 /**
94  * udisks_linux_partition_table_new:
95  *
96  * Creates a new #UDisksLinuxPartitionTable instance.
97  *
98  * Returns: A new #UDisksLinuxPartitionTable. Free with g_object_unref().
99  */
100 UDisksPartitionTable *
udisks_linux_partition_table_new(void)101 udisks_linux_partition_table_new (void)
102 {
103   return UDISKS_PARTITION_TABLE (g_object_new (UDISKS_TYPE_LINUX_PARTITION_TABLE,
104                                                NULL));
105 }
106 
107 /* ---------------------------------------------------------------------------------------------------- */
108 
109 /**
110  * udisks_linux_partition_table_update:
111  * @table: A #UDisksLinuxPartitionTable.
112  * @object: The enclosing #UDisksLinuxBlockObject instance.
113  *
114  * Updates the interface.
115  */
116 void
udisks_linux_partition_table_update(UDisksLinuxPartitionTable * table,UDisksLinuxBlockObject * object)117 udisks_linux_partition_table_update (UDisksLinuxPartitionTable  *table,
118                                      UDisksLinuxBlockObject     *object)
119 {
120   const gchar *type = NULL;
121   UDisksLinuxDevice *device = NULL;
122   UDisksDaemon *daemon = NULL;
123   guint num_parts = 0;
124   const gchar **partition_object_paths = NULL;
125   GList *partition_objects = NULL;
126   GList *object_p = NULL;
127   guint i = 0;
128 
129   /* update partition table type */
130   device = udisks_linux_block_object_get_device (object);
131   if (device != NULL)
132     type = g_udev_device_get_property (device->udev_device, "ID_PART_TABLE_TYPE");
133 
134   udisks_partition_table_set_type_ (UDISKS_PARTITION_TABLE (table), type);
135 
136   /* update list of partitions */
137   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
138 
139   partition_objects = udisks_linux_partition_table_get_partitions (daemon, UDISKS_PARTITION_TABLE (table), &num_parts);
140 
141   partition_object_paths = g_new0 (const gchar *, num_parts + 1);
142   for (i = 0, object_p = partition_objects; object_p != NULL; object_p = object_p->next, i++)
143     {
144       partition_object_paths[i] = g_dbus_object_get_object_path (g_dbus_interface_get_object (G_DBUS_INTERFACE (object_p->data)));
145     }
146 
147   udisks_partition_table_set_partitions (UDISKS_PARTITION_TABLE (table),
148                                          partition_object_paths);
149   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (table));
150 
151 
152   g_free (partition_object_paths);
153   g_clear_object (&device);
154   g_list_free_full (partition_objects, g_object_unref);
155 }
156 
157 /* ---------------------------------------------------------------------------------------------------- */
158 
159 GList *
udisks_linux_partition_table_get_partitions(UDisksDaemon * daemon,UDisksPartitionTable * table,guint * num_partitions)160 udisks_linux_partition_table_get_partitions (UDisksDaemon         *daemon,
161                                              UDisksPartitionTable *table,
162                                              guint                *num_partitions)
163 {
164   GList *ret = NULL;
165   GDBusObject *table_object;
166   const gchar *table_object_path;
167   GList *l, *object_proxies = NULL;
168   *num_partitions = 0;
169 
170   table_object = g_dbus_interface_get_object (G_DBUS_INTERFACE (table));
171   if (table_object == NULL)
172     goto out;
173   table_object_path = g_dbus_object_get_object_path (table_object);
174 
175   object_proxies = udisks_daemon_get_objects (daemon);
176   for (l = object_proxies; l != NULL; l = l->next)
177     {
178       UDisksObject *object = UDISKS_OBJECT (l->data);
179       UDisksPartition *partition;
180 
181       partition = udisks_object_get_partition (object);
182       if (partition == NULL)
183         continue;
184 
185       if (g_strcmp0 (udisks_partition_get_table (partition), table_object_path) == 0)
186         {
187           ret = g_list_prepend (ret, g_object_ref (partition));
188           (*num_partitions)++;
189         }
190 
191       g_object_unref (partition);
192     }
193   ret = g_list_reverse (ret);
194  out:
195   g_list_free_full (object_proxies, g_object_unref);
196   return ret;
197 }
198 
199 /* ---------------------------------------------------------------------------------------------------- */
200 
201 typedef struct
202 {
203   UDisksObject *partition_table_object;
204   guint64       pos_to_wait_for;
205   gboolean      ignore_container;
206 } WaitForPartitionData;
207 
208 static UDisksObject *
wait_for_partition(UDisksDaemon * daemon,gpointer user_data)209 wait_for_partition (UDisksDaemon *daemon,
210                     gpointer      user_data)
211 {
212   WaitForPartitionData *data = user_data;
213   UDisksObject *ret = NULL;
214   GList *objects, *l;
215 
216   objects = udisks_daemon_get_objects (daemon);
217   for (l = objects; l != NULL; l = l->next)
218     {
219       UDisksObject *object = UDISKS_OBJECT (l->data);
220       UDisksPartition *partition = udisks_object_get_partition (object);
221       if (partition != NULL)
222         {
223           if (g_strcmp0 (udisks_partition_get_table (partition),
224                          g_dbus_object_get_object_path (G_DBUS_OBJECT (data->partition_table_object))) == 0)
225             {
226               guint64 offset = udisks_partition_get_offset (partition);
227               guint64 size = udisks_partition_get_size (partition);
228 
229               if (data->pos_to_wait_for >= offset && data->pos_to_wait_for < offset + size)
230                 {
231                   if (!(udisks_partition_get_is_container (partition) && data->ignore_container))
232                     {
233                       g_object_unref (partition);
234                       ret = g_object_ref (object);
235                       goto out;
236                     }
237                 }
238             }
239           g_object_unref (partition);
240         }
241     }
242 
243  out:
244   g_list_free_full (objects, g_object_unref);
245   return ret;
246 }
247 
248 #define MIB_SIZE (1048576L)
249 
250 static UDisksObject *
udisks_linux_partition_table_handle_create_partition(UDisksPartitionTable * table,GDBusMethodInvocation * invocation,guint64 offset,guint64 size,const gchar * type,const gchar * name,GVariant * options)251 udisks_linux_partition_table_handle_create_partition (UDisksPartitionTable   *table,
252                                                       GDBusMethodInvocation  *invocation,
253                                                       guint64                 offset,
254                                                       guint64                 size,
255                                                       const gchar            *type,
256                                                       const gchar            *name,
257                                                       GVariant               *options)
258 {
259   const gchar *action_id = NULL;
260   const gchar *message = NULL;
261   UDisksBlock *block = NULL;
262   UDisksObject *object = NULL;
263   UDisksDaemon *daemon = NULL;
264   gchar *device_name = NULL;
265   WaitForPartitionData *wait_data = NULL;
266   UDisksObject *partition_object = NULL;
267   UDisksBlock *partition_block = NULL;
268   BDPartSpec *part_spec = NULL;
269   BDPartSpec *overlapping_part = NULL;
270   BDPartTypeReq part_type = 0;
271   gchar *table_type = NULL;
272   uid_t caller_uid;
273   GError *error = NULL;
274   UDisksBaseJob *job = NULL;
275   const gchar *partition_type = NULL;
276 
277   object = udisks_daemon_util_dup_object (table, &error);
278   if (object == NULL)
279     {
280       g_dbus_method_invocation_take_error (invocation, error);
281       goto out;
282     }
283 
284   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
285 
286   g_variant_lookup (options, "partition-type", "&s", &partition_type);
287 
288   block = udisks_object_get_block (object);
289   if (block == NULL)
290     {
291       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
292                                              "Partition table object is not a block device");
293       goto out;
294     }
295 
296   error = NULL;
297   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
298                                                invocation,
299                                                NULL /* GCancellable */,
300                                                &caller_uid,
301                                                &error))
302     {
303       g_dbus_method_invocation_return_gerror (invocation, error);
304       g_clear_error (&error);
305       goto out;
306     }
307 
308   action_id = "org.freedesktop.udisks2.modify-device";
309   /* Translators: Shown in authentication dialog when the user
310    * requests creating a new partition.
311    *
312    * Do not translate $(drive), it's a placeholder and
313    * will be replaced by the name of the drive/device in question
314    */
315   message = N_("Authentication is required to create a partition on $(drive)");
316   if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
317     {
318       if (udisks_block_get_hint_system (block))
319         {
320           action_id = "org.freedesktop.udisks2.modify-device-system";
321         }
322       else if (!udisks_daemon_util_on_user_seat (daemon, object, caller_uid))
323         {
324           action_id = "org.freedesktop.udisks2.modify-device-other-seat";
325         }
326     }
327 
328   if (!udisks_daemon_util_check_authorization_sync (daemon,
329                                                     object,
330                                                     action_id,
331                                                     options,
332                                                     message,
333                                                     invocation))
334     goto out;
335 
336   device_name = g_strdup (udisks_block_get_device (block));
337 
338   table_type = udisks_partition_table_dup_type_ (table);
339   wait_data = g_new0 (WaitForPartitionData, 1);
340   if (g_strcmp0 (table_type, "dos") == 0)
341     {
342       char *endp;
343       gint type_as_int;
344 
345       if (strlen (name) > 0)
346         {
347           g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
348                                                  "MBR partition table does not support names");
349           goto out;
350         }
351 
352       type_as_int = strtol (type, &endp, 0);
353 
354       /* Determine whether we are creating a primary, extended or logical partition */
355       if (partition_type != NULL)
356         {
357           if (g_strcmp0 (partition_type, "primary") == 0)
358             {
359               part_type = BD_PART_TYPE_REQ_NORMAL;
360             }
361           else if (g_strcmp0 (partition_type, "extended") == 0)
362             {
363               part_type = BD_PART_TYPE_REQ_EXTENDED;
364             }
365           else if (g_strcmp0 (partition_type, "logical") == 0)
366             {
367               part_type = BD_PART_TYPE_REQ_LOGICAL;
368             }
369           else
370             {
371               g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
372                                                      "Don't know how to create partition of type `%s'",
373                                                      partition_type);
374               goto out;
375             }
376         }
377       else if (type[0] != '\0' && *endp == '\0' &&
378                (type_as_int == 0x05 || type_as_int == 0x0f || type_as_int == 0x85))
379         {
380           part_type = BD_PART_TYPE_REQ_EXTENDED;
381         }
382       else
383         part_type = BD_PART_TYPE_REQ_NEXT;
384     }
385   else if (g_strcmp0 (table_type, "gpt") == 0)
386     {
387       part_type = BD_PART_TYPE_REQ_NORMAL;
388     }
389   else
390     {
391       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
392                                              "Don't know how to create partitions this partition table of type `%s'",
393                                              table_type);
394       goto out;
395     }
396 
397   job = udisks_daemon_launch_simple_job (daemon,
398                                          UDISKS_OBJECT (object),
399                                          "partition-create",
400                                          caller_uid,
401                                          NULL);
402 
403   if (job == NULL)
404     {
405       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
406                                              "Failed to create a job object");
407       goto out;
408     }
409 
410   /* Users might want to specify logical partitions start and size using size of
411    * of the extended partition. If this happens we need to shift start (offset)
412    * of the logical partition.
413    * XXX: We really shouldn't allow creation of overlapping partitions and
414    *      and just automatically fix this. But udisks currently doesn't have
415    *      functions to get free regions on the disks so this is a somewhat valid
416    *      use case. But we should definitely provide some functionality to get
417    *      right "numbers" and stop doing this.
418   */
419   overlapping_part = bd_part_get_part_by_pos (device_name, offset, &error);
420   if (overlapping_part != NULL && ! (overlapping_part->type & BD_PART_TYPE_FREESPACE))
421     {
422       /* extended partition or metadata of the extended partition */
423       if (overlapping_part->type & BD_PART_TYPE_EXTENDED || overlapping_part->type & (BD_PART_TYPE_LOGICAL | BD_PART_TYPE_METADATA))
424         {
425           if (overlapping_part->start == offset)
426             {
427               /* just add 1 byte, libblockdev will adjust it */
428               offset += 1;
429               udisks_warning ("Requested start of the logical partition overlaps "
430                               "with extended partition metadata. Start of the "
431                               "partition moved to %"G_GUINT64_FORMAT".", offset);
432             }
433         }
434       else
435         {
436           /* overlapping partition is not a free space nor an extended part -> error */
437           g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
438                                                  "Requested start for the new partition %"G_GUINT64_FORMAT" "
439                                                  "overlaps with existing partition %s.",
440                                                   offset, overlapping_part->path);
441           goto out;
442         }
443     }
444   else
445     g_clear_error (&error);
446 
447   part_spec = bd_part_create_part (device_name, part_type, offset,
448                                    size, BD_PART_ALIGN_OPTIMAL, &error);
449   if (!part_spec)
450     {
451       g_dbus_method_invocation_return_error (invocation,
452                                              UDISKS_ERROR,
453                                              UDISKS_ERROR_FAILED,
454                                              "Error creating partition on %s: %s",
455                                              udisks_block_get_device (block),
456                                              error->message);
457       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
458       goto out;
459     }
460 
461   /* set name if given */
462   if (g_strcmp0 (table_type, "gpt") == 0 && strlen (name) > 0)
463     {
464       if (!bd_part_set_part_name (device_name, part_spec->path, name, &error))
465         {
466           g_prefix_error (&error, "Error setting name for newly created partition: ");
467           g_dbus_method_invocation_take_error (invocation, error);
468           udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
469           goto out;
470         }
471     }
472 
473   /* set type if given and if not extended partition */
474   if (part_spec->type != BD_PART_TYPE_EXTENDED && strlen (type) > 0)
475     {
476       gboolean ret = FALSE;
477 
478       if (g_strcmp0 (table_type, "gpt") == 0)
479           ret = bd_part_set_part_type (device_name, part_spec->path, type, &error);
480       else if (g_strcmp0 (table_type, "dos") == 0)
481           ret = bd_part_set_part_id (device_name, part_spec->path, type, &error);
482 
483       if (!ret)
484         {
485           g_prefix_error (&error, "Error setting type for newly created partition: ");
486           g_dbus_method_invocation_take_error (invocation, error);
487           udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
488           goto out;
489         }
490     }
491 
492   /* wipe the newly created partition if wanted */
493   if (part_spec->type != BD_PART_TYPE_EXTENDED)
494     {
495       if (!bd_fs_wipe (part_spec->path, TRUE, &error))
496         {
497           if (g_error_matches (error, BD_FS_ERROR, BD_FS_ERROR_NOFS))
498             g_clear_error (&error);
499           else
500             {
501               g_dbus_method_invocation_return_error (invocation,
502                                                      UDISKS_ERROR,
503                                                      UDISKS_ERROR_FAILED,
504                                                      "Error wiping newly created partition %s: %s",
505                                                      part_spec->path,
506                                                      error->message);
507               udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
508               goto out;
509             }
510         }
511     }
512 
513   wait_data->ignore_container = (part_spec->type == BD_PART_TYPE_LOGICAL);
514   wait_data->pos_to_wait_for = part_spec->start + (part_spec->size / 2L);
515 
516   /* sit and wait for the partition to show up */
517   g_warn_if_fail (wait_data->pos_to_wait_for > 0);
518   wait_data->partition_table_object = object;
519   error = NULL;
520   partition_object = udisks_daemon_wait_for_object_sync (daemon,
521                                                          wait_for_partition,
522                                                          wait_data,
523                                                          NULL,
524                                                          UDISKS_DEFAULT_WAIT_TIMEOUT,
525                                                          &error);
526   if (partition_object == NULL)
527     {
528       g_prefix_error (&error, "Error waiting for partition to appear: ");
529       g_dbus_method_invocation_take_error (invocation, error);
530       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
531       goto out;
532     }
533   partition_block = udisks_object_get_block (partition_object);
534   if (partition_block == NULL)
535     {
536       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
537                                              "Partition object is not a block device");
538       g_clear_object (&partition_object);
539       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, NULL);
540       goto out;
541     }
542 
543   /* Trigger uevent on the disk -- we sometimes get add-remove-add uevents for
544      the new partition without getting change event for the disks after the
545      last add event and this breaks the "Partitions" property on the
546      "PartitionTable" interface. */
547   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
548                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
549 
550   udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
551 
552  out:
553   g_free (table_type);
554   g_free (wait_data);
555   g_clear_error (&error);
556   g_clear_object (&partition_block);
557   g_free (device_name);
558   g_clear_object (&object);
559   g_clear_object (&block);
560   if (part_spec)
561     bd_part_spec_free (part_spec);
562   if (overlapping_part)
563     bd_part_spec_free (overlapping_part);
564   return partition_object;
565 }
566 
567 static int
flock_block_dev(UDisksPartitionTable * iface)568 flock_block_dev (UDisksPartitionTable *iface)
569 {
570   UDisksObject *object = udisks_daemon_util_dup_object (iface, NULL);
571   UDisksBlock *block = object? udisks_object_peek_block (object) : NULL;
572   int fd = block? open (udisks_block_get_device (block), O_RDONLY) : -1;
573 
574   if (fd >= 0)
575     flock (fd, LOCK_SH | LOCK_NB);
576 
577   g_clear_object (&object);
578   return fd;
579 }
580 
581 static void
unflock_block_dev(int fd)582 unflock_block_dev (int fd)
583 {
584   if (fd >= 0)
585     close (fd);
586 }
587 
588 /* runs in thread dedicated to handling @invocation */
589 static gboolean
handle_create_partition(UDisksPartitionTable * table,GDBusMethodInvocation * invocation,guint64 offset,guint64 size,const gchar * type,const gchar * name,GVariant * options)590 handle_create_partition (UDisksPartitionTable   *table,
591                          GDBusMethodInvocation  *invocation,
592                          guint64                 offset,
593                          guint64                 size,
594                          const gchar            *type,
595                          const gchar            *name,
596                          GVariant               *options)
597 {
598   /* We (try to) take a shared lock on the partition table while
599      creating and formatting a new partition, here and also in
600      handle_create_partition_and_format.
601 
602      This lock prevents udevd from issuing a BLKRRPART ioctl call.
603      That ioctl is undesired because it might temporarily remove the
604      block device of the newly created block device.  It does so only
605      temporarily, but it still happens that the block device is
606      missing exactly when wipefs or mkfs try to access it.
607 
608      Also, a pair of remove/add events will cause udisks to create a
609      new internal UDisksObject to represent the block device of the
610      partition.  The code currently doesn't handle this and waits for
611      changes (such as an expected filesystem type or UUID) to a
612      obsolete internal object that will never see them.
613   */
614 
615   int fd = flock_block_dev (table);
616   UDisksObject *partition_object =
617     udisks_linux_partition_table_handle_create_partition (table,
618                                                           invocation,
619                                                           offset,
620                                                           size,
621                                                           type,
622                                                           name,
623                                                           options);
624 
625   if (partition_object)
626     {
627       udisks_partition_table_complete_create_partition
628         (table, invocation, g_dbus_object_get_object_path (G_DBUS_OBJECT (partition_object)));
629       g_object_unref (partition_object);
630     }
631 
632   unflock_block_dev (fd);
633 
634   return TRUE; /* returning TRUE means that we handled the method invocation */
635 }
636 
637 /* runs in thread dedicated to handling @invocation */
638 struct FormatCompleteData {
639   UDisksPartitionTable *table;
640   GDBusMethodInvocation *invocation;
641   UDisksObject *partition_object;
642   int lock_fd;
643 };
644 
645 static void
handle_format_complete(gpointer user_data)646 handle_format_complete (gpointer user_data)
647 {
648   struct FormatCompleteData *data = user_data;
649   udisks_partition_table_complete_create_partition
650     (data->table, data->invocation, g_dbus_object_get_object_path (G_DBUS_OBJECT (data->partition_object)));
651   unflock_block_dev (data->lock_fd);
652 }
653 
654 static gboolean
handle_create_partition_and_format(UDisksPartitionTable * table,GDBusMethodInvocation * invocation,guint64 offset,guint64 size,const gchar * type,const gchar * name,GVariant * options,const gchar * format_type,GVariant * format_options)655 handle_create_partition_and_format (UDisksPartitionTable   *table,
656                                     GDBusMethodInvocation  *invocation,
657                                     guint64                 offset,
658                                     guint64                 size,
659                                     const gchar            *type,
660                                     const gchar            *name,
661                                     GVariant               *options,
662                                     const gchar            *format_type,
663                                     GVariant               *format_options)
664 {
665   /* See handle_create_partition for a motivation of taking the lock.
666    */
667 
668   int fd = flock_block_dev (table);
669   UDisksObject *partition_object =
670     udisks_linux_partition_table_handle_create_partition (table,
671                                                           invocation,
672                                                           offset,
673                                                           size,
674                                                           type,
675                                                           name,
676                                                           options);
677 
678   if (partition_object)
679     {
680       struct FormatCompleteData data;
681       data.table = table;
682       data.invocation = invocation;
683       data.partition_object = partition_object;
684       data.lock_fd = fd;
685       udisks_linux_block_handle_format (udisks_object_peek_block (partition_object),
686                                         invocation,
687                                         format_type,
688                                         format_options,
689                                         handle_format_complete, &data);
690       g_object_unref (partition_object);
691     }
692   else
693     unflock_block_dev (fd);
694 
695   return TRUE; /* returning TRUE means that we handled the method invocation */
696 }
697 
698 /* ---------------------------------------------------------------------------------------------------- */
699 
700 static void
partition_table_iface_init(UDisksPartitionTableIface * iface)701 partition_table_iface_init (UDisksPartitionTableIface *iface)
702 {
703   iface->handle_create_partition = handle_create_partition;
704   iface->handle_create_partition_and_format = handle_create_partition_and_format;
705 }
706