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 #define _GNU_SOURCE /* for O_DIRECT */
22 
23 #include "config.h"
24 #include <glib/gi18n-lib.h>
25 
26 #include <sys/types.h>
27 #include <sys/mount.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <pwd.h>
31 #include <grp.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <mntent.h>
36 
37 #include <glib/gstdio.h>
38 #include <gio/gunixfdlist.h>
39 
40 #include <libmount/libmount.h>
41 
42 #include <blockdev/part.h>
43 #include <blockdev/fs.h>
44 #include <blockdev/crypto.h>
45 
46 #include "udiskslogging.h"
47 #include "udiskslinuxblock.h"
48 #include "udiskslinuxblockobject.h"
49 #include "udiskslinuxdriveobject.h"
50 #include "udiskslinuxfsinfo.h"
51 #include "udisksdaemon.h"
52 #include "udisksstate.h"
53 #include "udisksprivate.h"
54 #include "udisksconfigmanager.h"
55 #include "udisksdaemonutil.h"
56 #include "udiskslinuxprovider.h"
57 #include "udisksfstabentry.h"
58 #include "udiskscrypttabmonitor.h"
59 #include "udiskscrypttabentry.h"
60 #include "udisksdaemonutil.h"
61 #include "udisksbasejob.h"
62 #include "udiskssimplejob.h"
63 #include "udiskslinuxdriveata.h"
64 #include "udiskslinuxmdraidobject.h"
65 #include "udiskslinuxdevice.h"
66 #include "udiskslinuxpartition.h"
67 #include "udiskslinuxencrypted.h"
68 #include "udiskslinuxencryptedhelpers.h"
69 #include "udiskslinuxpartitiontable.h"
70 #include "udiskslinuxfilesystemhelpers.h"
71 
72 #ifdef HAVE_LIBMOUNT_UTAB
73 #include "udisksutabmonitor.h"
74 #include "udisksutabentry.h"
75 #endif
76 
77 /**
78  * SECTION:udiskslinuxblock
79  * @title: UDisksLinuxBlock
80  * @short_description: Linux implementation of #UDisksBlock
81  *
82  * This type provides an implementation of the #UDisksBlock
83  * interface on Linux.
84  */
85 
86 typedef struct _UDisksLinuxBlockClass   UDisksLinuxBlockClass;
87 
88 /**
89  * UDisksLinuxBlock:
90  *
91  * The #UDisksLinuxBlock structure contains only private data and should
92  * only be accessed using the provided API.
93  */
94 struct _UDisksLinuxBlock
95 {
96   UDisksBlockSkeleton parent_instance;
97 
98   /* only allow single cryptsetup call at once */
99   GMutex encrypted_lock;
100 };
101 
102 struct _UDisksLinuxBlockClass
103 {
104   UDisksBlockSkeletonClass parent_class;
105 };
106 
107 static void block_iface_init (UDisksBlockIface *iface);
108 
109 G_DEFINE_TYPE_WITH_CODE (UDisksLinuxBlock, udisks_linux_block, UDISKS_TYPE_BLOCK_SKELETON,
110                          G_IMPLEMENT_INTERFACE (UDISKS_TYPE_BLOCK, block_iface_init));
111 
112 /* ---------------------------------------------------------------------------------------------------- */
113 
114 static void
udisks_linux_block_init(UDisksLinuxBlock * block)115 udisks_linux_block_init (UDisksLinuxBlock *block)
116 {
117   g_mutex_init (&(block->encrypted_lock));
118   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (block),
119                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
120 }
121 
122 static void
udisks_linux_block_finalize(GObject * object)123 udisks_linux_block_finalize (GObject *object)
124 {
125   UDisksLinuxBlock *block = UDISKS_LINUX_BLOCK (object);
126 
127   g_mutex_clear (&(block->encrypted_lock));
128 
129   if (G_OBJECT_CLASS (udisks_linux_block_parent_class)->finalize != NULL)
130     G_OBJECT_CLASS (udisks_linux_block_parent_class)->finalize (object);
131 }
132 
133 static void
udisks_linux_block_class_init(UDisksLinuxBlockClass * klass)134 udisks_linux_block_class_init (UDisksLinuxBlockClass *klass)
135 {
136   GObjectClass *gobject_class;
137 
138   gobject_class = G_OBJECT_CLASS (klass);
139   gobject_class->finalize = udisks_linux_block_finalize;
140 }
141 
142 /**
143  * udisks_linux_block_new:
144  *
145  * Creates a new #UDisksLinuxBlock instance.
146  *
147  * Returns: A new #UDisksLinuxBlock. Free with g_object_unref().
148  */
149 UDisksBlock *
udisks_linux_block_new(void)150 udisks_linux_block_new (void)
151 {
152   return UDISKS_BLOCK (g_object_new (UDISKS_TYPE_LINUX_BLOCK,
153                                      NULL));
154 }
155 
156 /* ---------------------------------------------------------------------------------------------------- */
157 
158 static gchar *
get_sysfs_attr(GUdevDevice * device,const gchar * attr)159 get_sysfs_attr (GUdevDevice *device,
160                 const gchar *attr)
161 {
162   gchar *filename = NULL;
163   gchar *value = NULL;
164   gboolean ret = FALSE;
165   GError *error = NULL;
166 
167   filename = g_strconcat (g_udev_device_get_sysfs_path (device),
168                           "/",
169                           attr,
170                           NULL);
171 
172 
173   ret = g_file_get_contents (filename,
174                              &value,
175                              NULL,
176                              &error);
177   if (!ret)
178     {
179       udisks_debug ("Failed to read sysfs attribute %s: %s", attr, error->message);
180       g_clear_error (&error);
181     }
182 
183   g_free (filename);
184   return value;
185 }
186 
187 /* ---------------------------------------------------------------------------------------------------- */
188 
189 static UDisksLinuxBlockObject *
find_block_device_by_sysfs_path(GDBusObjectManagerServer * object_manager,const gchar * sysfs_path)190 find_block_device_by_sysfs_path (GDBusObjectManagerServer *object_manager,
191                                  const gchar              *sysfs_path)
192 {
193   UDisksLinuxBlockObject *ret;
194   GList *objects;
195   GList *l;
196 
197   ret = NULL;
198 
199   objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
200   for (l = objects; l != NULL; l = l->next)
201     {
202       GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (l->data);
203       UDisksLinuxDevice *device;
204 
205       if (!UDISKS_IS_LINUX_BLOCK_OBJECT (object))
206         continue;
207 
208       device = udisks_linux_block_object_get_device (UDISKS_LINUX_BLOCK_OBJECT (object));
209       if (g_strcmp0 (sysfs_path, g_udev_device_get_sysfs_path (device->udev_device)) == 0)
210         {
211           ret = g_object_ref (UDISKS_LINUX_BLOCK_OBJECT (object));
212           g_object_unref (device);
213           goto out;
214         }
215       g_object_unref (device);
216     }
217 
218  out:
219   g_list_free_full (objects, g_object_unref);
220   return ret;
221 }
222 
223 /* ---------------------------------------------------------------------------------------------------- */
224 
225 static gchar *
find_drive(GDBusObjectManagerServer * object_manager,GUdevDevice * block_device,UDisksDrive ** out_drive)226 find_drive (GDBusObjectManagerServer  *object_manager,
227             GUdevDevice               *block_device,
228             UDisksDrive              **out_drive)
229 {
230   GUdevDevice *whole_disk_block_device;
231   const gchar *whole_disk_block_device_sysfs_path;
232   gchar *ret;
233   GList *objects;
234   GList *l;
235 
236   ret = NULL;
237 
238   if (g_strcmp0 (g_udev_device_get_devtype (block_device), "disk") == 0)
239     whole_disk_block_device = g_object_ref (block_device);
240   else
241     whole_disk_block_device = g_udev_device_get_parent_with_subsystem (block_device, "block", "disk");
242   whole_disk_block_device_sysfs_path = g_udev_device_get_sysfs_path (whole_disk_block_device);
243 
244   objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
245   for (l = objects; l != NULL; l = l->next)
246     {
247       GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (l->data);
248       GList *drive_devices;
249       GList *j;
250 
251       if (!UDISKS_IS_LINUX_DRIVE_OBJECT (object))
252         continue;
253 
254       drive_devices = udisks_linux_drive_object_get_devices (UDISKS_LINUX_DRIVE_OBJECT (object));
255       for (j = drive_devices; j != NULL; j = j->next)
256         {
257           UDisksLinuxDevice *drive_device = UDISKS_LINUX_DEVICE (j->data);
258           const gchar *drive_sysfs_path;
259 
260           drive_sysfs_path = g_udev_device_get_sysfs_path (drive_device->udev_device);
261           if (g_strcmp0 (whole_disk_block_device_sysfs_path, drive_sysfs_path) == 0)
262             {
263               if (out_drive != NULL)
264                 *out_drive = udisks_object_get_drive (UDISKS_OBJECT (object));
265               ret = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
266               g_list_free_full (drive_devices, g_object_unref);
267               goto out;
268             }
269         }
270       g_list_free_full (drive_devices, g_object_unref);
271     }
272 
273  out:
274   g_list_free_full (objects, g_object_unref);
275   g_object_unref (whole_disk_block_device);
276   return ret;
277 }
278 
279 /* ---------------------------------------------------------------------------------------------------- */
280 
281 static UDisksLinuxMDRaidObject *
find_mdraid(GDBusObjectManagerServer * object_manager,const gchar * md_uuid)282 find_mdraid (GDBusObjectManagerServer  *object_manager,
283              const gchar               *md_uuid)
284 {
285   UDisksLinuxMDRaidObject *ret = NULL;
286   GList *objects = NULL, *l;
287 
288   objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
289   for (l = objects; l != NULL; l = l->next)
290     {
291       GDBusObjectSkeleton *object = G_DBUS_OBJECT_SKELETON (l->data);
292       if (UDISKS_IS_LINUX_MDRAID_OBJECT (object))
293         {
294           UDisksMDRaid *mdraid = udisks_object_get_mdraid (UDISKS_OBJECT (object));
295           if (mdraid != NULL)
296             {
297               if (g_strcmp0 (udisks_mdraid_get_uuid (mdraid), md_uuid) == 0)
298                 {
299                   ret = UDISKS_LINUX_MDRAID_OBJECT (g_object_ref (object));
300                   g_object_unref (mdraid);
301                   goto out;
302                 }
303               g_object_unref (mdraid);
304             }
305         }
306     }
307 
308  out:
309   g_list_free_full (objects, g_object_unref);
310   return ret;
311 }
312 
313 /* ---------------------------------------------------------------------------------------------------- */
314 
315 static void
update_mdraid(UDisksLinuxBlock * block,UDisksLinuxDevice * device,UDisksDrive * drive,GDBusObjectManagerServer * object_manager)316 update_mdraid (UDisksLinuxBlock         *block,
317                UDisksLinuxDevice        *device,
318                UDisksDrive              *drive,
319                GDBusObjectManagerServer *object_manager)
320 {
321   UDisksBlock *iface = UDISKS_BLOCK (block);
322   const gchar *uuid;
323   const gchar *objpath_mdraid = "/";
324   const gchar *objpath_mdraid_member = "/";
325   UDisksLinuxMDRaidObject *object = NULL;
326 
327   uuid = g_udev_device_get_property (device->udev_device, "UDISKS_MD_UUID");
328   if (uuid != NULL && strlen (uuid) > 0)
329     {
330       object = find_mdraid (object_manager, uuid);
331       if (object != NULL)
332         {
333           objpath_mdraid = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
334           g_clear_object (&object);
335         }
336     }
337 
338   uuid = g_udev_device_get_property (device->udev_device, "UDISKS_MD_MEMBER_UUID");
339   if (uuid != NULL && strlen (uuid) > 0)
340     {
341       object = find_mdraid (object_manager, uuid);
342       if (object != NULL)
343         {
344           objpath_mdraid_member = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
345           g_clear_object (&object);
346         }
347     }
348 
349   udisks_block_set_mdraid (iface, objpath_mdraid);
350   udisks_block_set_mdraid_member (iface, objpath_mdraid_member);
351 }
352 
353 /* ---------------------------------------------------------------------------------------------------- */
354 
355 /**
356  * udisks_linux_block_matches_id:
357  * @block: A #UDisksLinuxBlock.
358  * @device_path: A device path string.
359  *
360  * Compares block device identifiers and returns %TRUE if match is found. The @device_path
361  * argument may be a device file or a common KEY=VALUE identifier as used e.g. in /etc/fstab
362  * or /etc/crypttab.
363  *
364  * Returns: %TRUE when identifiers do match, %FALSE otherwise.
365  */
366 gboolean
udisks_linux_block_matches_id(UDisksLinuxBlock * block,const gchar * device_path)367 udisks_linux_block_matches_id (UDisksLinuxBlock *block,
368                                const gchar      *device_path)
369 {
370   const gchar *device = NULL;
371   const gchar *label = NULL;
372   const gchar *uuid = NULL;
373   const gchar *partuuid = NULL;
374   const gchar *partlabel = NULL;
375   const gchar *const *symlinks;
376 
377   if (device_path == NULL || strlen (device_path) < 1)
378     {
379       return FALSE;
380     }
381   if (g_str_has_prefix (device_path, "UUID="))
382     {
383       uuid = device_path + 5;
384     }
385   else if (g_str_has_prefix (device_path, "LABEL="))
386     {
387       label = device_path + 6;
388     }
389   else if (g_str_has_prefix (device_path, "PARTUUID="))
390     {
391       partuuid = device_path + 9;
392     }
393   else if (g_str_has_prefix (device_path, "PARTLABEL="))
394     {
395       partlabel = device_path + 10;
396     }
397   else if (g_str_has_prefix (device_path, "/dev"))
398     {
399       device = device_path;
400     }
401   else
402     {
403       /* ignore non-device entry */
404       return FALSE;
405     }
406 
407   if (device != NULL)
408     {
409       if (g_strcmp0 (device, udisks_block_get_device (UDISKS_BLOCK (block))) == 0)
410         return TRUE;
411 
412       symlinks = udisks_block_get_symlinks (UDISKS_BLOCK (block));
413       if (symlinks && g_strv_contains (symlinks, device))
414         return TRUE;
415     }
416   if (label != NULL && g_strcmp0 (label, udisks_block_get_id_label (UDISKS_BLOCK (block))) == 0)
417     {
418       return TRUE;
419     }
420   if (uuid != NULL && g_strcmp0 (uuid, udisks_block_get_id_uuid (UDISKS_BLOCK (block))) == 0)
421     {
422       return TRUE;
423     }
424   if (partlabel != NULL || partuuid != NULL)
425     {
426       UDisksLinuxBlockObject *object;
427       UDisksLinuxDevice *linux_device;
428 
429       object = udisks_daemon_util_dup_object (block, NULL);
430       if (object != NULL)
431         {
432           linux_device = udisks_linux_block_object_get_device (object);
433           g_clear_object (&object);
434           if (linux_device != NULL && linux_device->udev_device != NULL &&
435               ((partuuid != NULL  && g_strcmp0 (partuuid,  g_udev_device_get_property (linux_device->udev_device, "ID_PART_ENTRY_UUID")) == 0) ||
436                (partlabel != NULL && g_strcmp0 (partlabel, g_udev_device_get_property (linux_device->udev_device, "ID_PART_ENTRY_NAME")) == 0)))
437             {
438               g_object_unref (linux_device);
439               return TRUE;
440             }
441           g_clear_object (&linux_device);
442         }
443     }
444 
445   return FALSE;
446 }
447 
448 static GList *
find_fstab_entries(UDisksDaemon * daemon,UDisksLinuxBlock * block,const gchar * needle)449 find_fstab_entries (UDisksDaemon     *daemon,
450                     UDisksLinuxBlock *block,
451                     const gchar      *needle)
452 {
453   struct libmnt_table *table;
454   struct libmnt_iter* iter;
455   struct libmnt_fs *fs = NULL;
456   GList *ret = NULL;
457 
458   table = mnt_new_table ();
459   if (mnt_table_parse_fstab (table, NULL) < 0)
460     {
461       mnt_free_table (table);
462       return NULL;
463     }
464 
465   iter = mnt_new_iter (MNT_ITER_FORWARD);
466   while (mnt_table_next_fs (table, iter, &fs) == 0)
467     {
468       UDisksFstabEntry *entry;
469 
470       if (block != NULL)
471         {
472           if (! udisks_linux_block_matches_id (block, mnt_fs_get_source (fs)))
473             continue;
474         }
475       else if (needle != NULL)
476         {
477           const char *opts;
478 
479           opts = mnt_fs_get_options (fs);
480           if (! opts || g_strstr_len (opts, -1, needle) == NULL)
481             continue;
482         }
483 
484       entry = _udisks_fstab_entry_new_from_mnt_fs (fs);
485       ret = g_list_prepend (ret, entry);
486     }
487   mnt_free_iter (iter);
488   mnt_free_table (table);
489 
490   return g_list_reverse (ret);
491 }
492 
493 static GList *
find_crypttab_entries_for_device(UDisksLinuxBlock * block,UDisksDaemon * daemon)494 find_crypttab_entries_for_device (UDisksLinuxBlock *block,
495                                   UDisksDaemon     *daemon)
496 {
497   GList *entries;
498   GList *l;
499   GList *ret;
500 
501   ret = NULL;
502 
503   /* if this is too slow, we could add lookup methods to UDisksCrypttabMonitor... */
504   entries = udisks_crypttab_monitor_get_entries (udisks_daemon_get_crypttab_monitor (daemon));
505   for (l = entries; l != NULL; l = l->next)
506     {
507       UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (l->data);
508       const gchar *device;
509 
510       device = udisks_crypttab_entry_get_device (entry);
511       if (udisks_linux_block_matches_id (block, device))
512         ret = g_list_prepend (ret, g_object_ref (entry));
513     }
514 
515   g_list_free_full (entries, g_object_unref);
516   return ret;
517 }
518 
519 static GList *
find_crypttab_entries_for_needle(gchar * needle,UDisksDaemon * daemon)520 find_crypttab_entries_for_needle (gchar        *needle,
521                                   UDisksDaemon *daemon)
522 {
523   GList *entries;
524   GList *l;
525   GList *ret;
526 
527   ret = NULL;
528 
529   entries = udisks_crypttab_monitor_get_entries (udisks_daemon_get_crypttab_monitor (daemon));
530   for (l = entries; l != NULL; l = l->next)
531     {
532       UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (l->data);
533       const gchar *opts = NULL;
534 
535       opts = udisks_crypttab_entry_get_options (entry);
536       if (opts && strstr(opts, needle))
537         ret = g_list_prepend (ret, g_object_ref (entry));
538     }
539 
540   g_list_free_full (entries, g_object_unref);
541   return ret;
542 }
543 
544 #ifdef HAVE_LIBMOUNT_UTAB
545 static GList *
find_utab_entries_for_device(UDisksLinuxBlock * block,UDisksDaemon * daemon)546 find_utab_entries_for_device (UDisksLinuxBlock *block,
547                               UDisksDaemon     *daemon)
548 {
549   GSList *entries, *l;
550   GList *ret;
551 
552   ret = NULL;
553 
554   /* if this is too slow, we could add lookup methods to UDisksUtabMonitor... */
555   entries = udisks_utab_monitor_get_entries (udisks_daemon_get_utab_monitor (daemon));
556   for (l = entries; l != NULL; l = l->next)
557     {
558       UDisksUtabEntry *entry = UDISKS_UTAB_ENTRY (l->data);
559       const gchar *source = udisks_utab_entry_get_source (entry);
560 
561       if (!g_str_has_prefix (source, "/dev"))
562         continue;
563 
564       if (udisks_linux_block_matches_id (block, source))
565         ret = g_list_prepend (ret, g_object_ref (entry));
566     }
567 
568   g_slist_free_full (entries, g_object_unref);
569   return ret;
570 }
571 #endif
572 
573 static void
add_fstab_entry(GVariantBuilder * builder,UDisksFstabEntry * entry)574 add_fstab_entry (GVariantBuilder  *builder,
575                  UDisksFstabEntry *entry)
576 {
577   GVariantBuilder dict_builder;
578   g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_VARDICT);
579   g_variant_builder_add (&dict_builder, "{sv}", "fsname",
580                          g_variant_new_bytestring (udisks_fstab_entry_get_fsname (entry)));
581   g_variant_builder_add (&dict_builder, "{sv}", "dir",
582                          g_variant_new_bytestring (udisks_fstab_entry_get_dir (entry)));
583   g_variant_builder_add (&dict_builder, "{sv}", "type",
584                          g_variant_new_bytestring (udisks_fstab_entry_get_fstype (entry)));
585   g_variant_builder_add (&dict_builder, "{sv}", "opts",
586                          g_variant_new_bytestring (udisks_fstab_entry_get_opts (entry)));
587   g_variant_builder_add (&dict_builder, "{sv}", "freq",
588                          g_variant_new_int32 (udisks_fstab_entry_get_freq (entry)));
589   g_variant_builder_add (&dict_builder, "{sv}", "passno",
590                          g_variant_new_int32 (udisks_fstab_entry_get_passno (entry)));
591   g_variant_builder_add (builder,
592                          "(sa{sv})",
593                          "fstab", &dict_builder);
594 }
595 
596 static gboolean
add_crypttab_entry(GVariantBuilder * builder,UDisksCrypttabEntry * entry,gboolean include_secrets,GError ** error)597 add_crypttab_entry (GVariantBuilder       *builder,
598                     UDisksCrypttabEntry   *entry,
599                     gboolean               include_secrets,
600                     GError               **error)
601 {
602   GVariantBuilder dict_builder;
603   const gchar *passphrase_path;
604   const gchar *options;
605   gchar *passphrase_contents;
606   gsize passphrase_contents_length;
607 
608   passphrase_path = udisks_crypttab_entry_get_passphrase_path (entry);
609   if (passphrase_path == NULL || g_strcmp0 (passphrase_path, "none") == 0)
610     passphrase_path = "";
611   passphrase_contents = NULL;
612   if (!(g_strcmp0 (passphrase_path, "") == 0 || g_str_has_prefix (passphrase_path, "/dev")))
613     {
614       if (include_secrets)
615         {
616           if (!g_file_get_contents (passphrase_path,
617                                     &passphrase_contents,
618                                     &passphrase_contents_length,
619                                     error))
620             {
621               g_prefix_error (error,
622                               "Error loading secrets from file `%s' referenced in /etc/crypttab entry: ",
623                               passphrase_path);
624               return FALSE;
625             }
626         }
627     }
628 
629   options = udisks_crypttab_entry_get_options (entry);
630   if (options == NULL)
631     options = "";
632 
633   g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_VARDICT);
634   g_variant_builder_add (&dict_builder, "{sv}", "name",
635                          g_variant_new_bytestring (udisks_crypttab_entry_get_name (entry)));
636   g_variant_builder_add (&dict_builder, "{sv}", "device",
637                          g_variant_new_bytestring (udisks_crypttab_entry_get_device (entry)));
638   g_variant_builder_add (&dict_builder, "{sv}", "passphrase-path",
639                          g_variant_new_bytestring (passphrase_path));
640   if (passphrase_contents != NULL)
641     {
642       g_variant_builder_add (&dict_builder, "{sv}", "passphrase-contents",
643                              g_variant_new_bytestring (passphrase_contents));
644     }
645   g_variant_builder_add (&dict_builder, "{sv}", "options",
646                          g_variant_new_bytestring (options));
647   g_variant_builder_add (builder,
648                          "(sa{sv})",
649                          "crypttab", &dict_builder);
650   if (passphrase_contents != NULL)
651     {
652       memset (passphrase_contents, '\0', passphrase_contents_length);
653       g_free (passphrase_contents);
654     }
655 
656   return TRUE;
657 }
658 
659 /* returns a floating GVariant */
660 static GVariant *
calculate_configuration(UDisksLinuxBlock * block,UDisksDaemon * daemon,gboolean include_secrets,GError ** error)661 calculate_configuration (UDisksLinuxBlock  *block,
662                          UDisksDaemon      *daemon,
663                          gboolean           include_secrets,
664                          GError           **error)
665 {
666   GList *entries;
667   GList *l;
668   GVariantBuilder builder;
669   GVariant *ret;
670 
671   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
672 
673   ret = NULL;
674 
675   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa{sv})"));
676   /* First the /etc/fstab entries */
677   entries = find_fstab_entries (daemon, block, NULL);
678   for (l = entries; l != NULL; l = l->next)
679     add_fstab_entry (&builder, UDISKS_FSTAB_ENTRY (l->data));
680   g_list_free_full (entries, g_object_unref);
681 
682   /* Then the /etc/crypttab entries (currently only supported for LUKS) */
683   if (udisks_linux_block_is_luks (UDISKS_BLOCK (block)))
684     {
685       entries = find_crypttab_entries_for_device (block, daemon);
686       for (l = entries; l != NULL; l = l->next)
687         {
688           if (!add_crypttab_entry (&builder, UDISKS_CRYPTTAB_ENTRY (l->data), include_secrets, error))
689             {
690               g_variant_builder_clear (&builder);
691               g_list_free_full (entries, g_object_unref);
692               goto out;
693             }
694         }
695       g_list_free_full (entries, g_object_unref);
696     }
697 
698   ret = g_variant_builder_end (&builder);
699 
700  out:
701   return ret;
702 }
703 
704 #ifdef HAVE_LIBMOUNT_UTAB
705 static gchar **
calculate_userspace_mount_options(UDisksLinuxBlock * block,UDisksDaemon * daemon)706 calculate_userspace_mount_options (UDisksLinuxBlock *block,
707                                    UDisksDaemon     *daemon)
708 {
709   GList *entries, *l;
710   GPtrArray *ret;
711 
712   ret = g_ptr_array_new ();
713 
714   /* Get the /run/mounts/utab entries */
715   entries = find_utab_entries_for_device (block, daemon);
716   for (l = entries; l != NULL; l = l->next) {
717     UDisksUtabEntry *entry = UDISKS_UTAB_ENTRY (l->data);
718     const gchar * const *opts = udisks_utab_entry_get_opts (entry);
719     for (gint i = 0; opts[i] != NULL; ++i)
720       g_ptr_array_add (ret, g_strdup (opts[i]));
721   }
722   g_ptr_array_add (ret, NULL);
723   g_list_free_full (entries, g_object_unref);
724 
725   return (gchar **) g_ptr_array_free (ret, FALSE);
726 }
727 #endif
728 
729 static void
update_configuration(UDisksLinuxBlock * block,UDisksDaemon * daemon)730 update_configuration (UDisksLinuxBlock  *block,
731                       UDisksDaemon      *daemon)
732 {
733   GVariant *configuration;
734   GError *error;
735 
736   error = NULL;
737   configuration = calculate_configuration (block, daemon, FALSE, &error);
738   if (configuration == NULL)
739     {
740       udisks_warning ("Error loading configuration: %s (%s, %d)",
741                       error->message, g_quark_to_string (error->domain), error->code);
742       g_clear_error (&error);
743       configuration = g_variant_new ("a(sa{sv})", NULL);
744     }
745   udisks_block_set_configuration (UDISKS_BLOCK (block), configuration);
746   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (block));
747 }
748 
749 #ifdef HAVE_LIBMOUNT_UTAB
750 static void
update_userspace_mount_options(UDisksLinuxBlock * block,UDisksDaemon * daemon)751 update_userspace_mount_options (UDisksLinuxBlock *block,
752                                 UDisksDaemon     *daemon)
753 {
754   gchar **opts;
755 
756   opts = calculate_userspace_mount_options (block, daemon);
757   udisks_block_set_userspace_mount_options (UDISKS_BLOCK (block), (const gchar * const*) opts);
758 
759   g_strfreev (opts);
760 }
761 #endif
762 
763 /* ---------------------------------------------------------------------------------------------------- */
764 
765 /* returns a floating GVariant */
766 static GVariant *
find_configurations(gchar * needle,UDisksDaemon * daemon,gboolean include_secrets,GError ** error)767 find_configurations (gchar         *needle,
768                      UDisksDaemon  *daemon,
769                      gboolean       include_secrets,
770                      GError       **error)
771 {
772   GList *entries;
773   GList *l;
774   GVariantBuilder builder;
775   GVariant *ret;
776 
777   udisks_debug ("Looking for %s", needle);
778 
779   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
780 
781   ret = NULL;
782 
783   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa{sv})"));
784   /* First the /etc/fstab entries */
785   entries = find_fstab_entries (daemon, NULL, needle);
786   for (l = entries; l != NULL; l = l->next)
787     add_fstab_entry (&builder, UDISKS_FSTAB_ENTRY (l->data));
788   g_list_free_full (entries, g_object_unref);
789 
790   /* Then the /etc/crypttab entries */
791   entries = find_crypttab_entries_for_needle (needle, daemon);
792   for (l = entries; l != NULL; l = l->next)
793     {
794       if (!add_crypttab_entry (&builder, UDISKS_CRYPTTAB_ENTRY (l->data), include_secrets, error))
795         {
796           g_variant_builder_clear (&builder);
797           g_list_free_full (entries, g_object_unref);
798           goto out;
799         }
800     }
801   g_list_free_full (entries, g_object_unref);
802 
803   ret = g_variant_builder_end (&builder);
804 
805  out:
806   return ret;
807 }
808 
809 GVariant *
udisks_linux_find_child_configuration(UDisksDaemon * daemon,const gchar * uuid)810 udisks_linux_find_child_configuration (UDisksDaemon *daemon,
811                                        const gchar  *uuid)
812 {
813   GError *error = NULL;
814   gchar *needle = g_strdup_printf ("x-parent=%s", uuid);
815   GVariant *res = find_configurations (needle, daemon, FALSE, &error);
816   if (res == NULL)
817     {
818       udisks_warning ("Error loading configuration: %s (%s, %d)",
819                       error->message, g_quark_to_string (error->domain), error->code);
820       g_clear_error (&error);
821       res = g_variant_new ("a(sa{sv})", NULL);
822     }
823   g_free (needle);
824   return res;
825 }
826 
827 /* ---------------------------------------------------------------------------------------------------- */
828 
829 static void
update_hints(UDisksDaemon * daemon,UDisksLinuxBlock * block,UDisksLinuxDevice * device,UDisksDrive * drive)830 update_hints (UDisksDaemon      *daemon,
831               UDisksLinuxBlock  *block,
832               UDisksLinuxDevice *device,
833               UDisksDrive       *drive)
834 {
835   UDisksBlock *iface = UDISKS_BLOCK (block);
836   gboolean hint_partitionable;
837   gboolean hint_system;
838   gboolean hint_ignore;
839   gboolean hint_auto;
840   const gchar *hint_name;
841   const gchar *hint_icon_name;
842   const gchar *hint_symbolic_icon_name;
843   const gchar *device_file;
844   GList *fstab_entries;
845   GList *l;
846 
847   /* very conservative defaults */
848   hint_partitionable = TRUE;
849   hint_system = TRUE;
850   hint_ignore = FALSE;
851   hint_auto = FALSE;
852   hint_name = NULL;
853   hint_icon_name = NULL;
854   hint_symbolic_icon_name = NULL;
855 
856   device_file = g_udev_device_get_device_file (device->udev_device);
857 
858   /* Provide easy access to _only_ the following devices
859    *
860    *  - anything connected via known local buses (e.g. USB or Firewire, MMC or MemoryStick)
861    *  - any device with removable media
862    *
863    * Be careful when extending this list as we don't want to automount
864    * the world when (inadvertently) connecting to a SAN.
865    */
866   if (drive != NULL)
867     {
868       const gchar *connection_bus;
869       gboolean removable;
870       connection_bus = udisks_drive_get_connection_bus (drive);
871       removable = udisks_drive_get_media_removable (drive);
872       if (removable ||
873           g_strcmp0 (connection_bus, "usb") == 0 ||
874           g_strcmp0 (connection_bus, "ieee1394") == 0 ||
875           g_str_has_prefix (device_file, "/dev/msblk") ||
876           g_str_has_prefix (device_file, "/dev/mspblk"))
877         {
878           hint_system = FALSE;
879           hint_auto = TRUE;
880         }
881     }
882 
883   /* Floppy drives are not partitionable and should never be auto-mounted */
884   if (g_str_has_prefix (device_file, "/dev/fd"))
885     {
886       hint_system = FALSE;
887       hint_partitionable = FALSE;
888       hint_auto = FALSE;
889     }
890 
891   /* CD-ROM media / drives are not partitionable, at least not here on Linux */
892   if (g_udev_device_get_property_as_boolean (device->udev_device, "ID_CDROM"))
893     hint_partitionable = FALSE;
894 
895   /* device-mapper devices are not partitionable (TODO: for multipath, they are via kpartx(8) hacks) */
896   if (g_str_has_prefix (g_udev_device_get_name (device->udev_device), "dm-"))
897     hint_partitionable = FALSE;
898 
899   /* Check fstab entries */
900   fstab_entries = find_fstab_entries (daemon, block, NULL);
901   for (l = fstab_entries; l != NULL; l = l->next)
902     {
903       UDisksFstabEntry *entry = UDISKS_FSTAB_ENTRY (l->data);
904       /* Honour the sysadmin-specified 'noauto' mount option */
905       if (udisks_fstab_entry_has_opt (entry, "+noauto"))
906         hint_auto = FALSE;
907     }
908   g_list_free_full (fstab_entries, g_object_unref);
909 
910   /* TODO: set ignore to TRUE for physical paths belonging to a drive with multiple paths */
911 
912   /* Override from UDISKS_* udev properties */
913   if (g_udev_device_has_property (device->udev_device, "UDISKS_SYSTEM"))
914     hint_system = g_udev_device_get_property_as_boolean (device->udev_device, "UDISKS_SYSTEM");
915 
916   if (g_udev_device_has_property (device->udev_device, "UDISKS_IGNORE"))
917     hint_ignore = g_udev_device_get_property_as_boolean (device->udev_device, "UDISKS_IGNORE");
918 
919   if (g_udev_device_has_property (device->udev_device, "UDISKS_AUTO"))
920     hint_auto = g_udev_device_get_property_as_boolean (device->udev_device, "UDISKS_AUTO");
921 
922   if (g_udev_device_has_property (device->udev_device, "UDISKS_NAME"))
923     hint_name = g_udev_device_get_property (device->udev_device, "UDISKS_NAME");
924 
925   if (g_udev_device_has_property (device->udev_device, "UDISKS_ICON_NAME"))
926     hint_icon_name = g_udev_device_get_property (device->udev_device, "UDISKS_ICON_NAME");
927 
928   if (g_udev_device_has_property (device->udev_device, "UDISKS_SYMBOLIC_ICON_NAME"))
929     hint_symbolic_icon_name = g_udev_device_get_property (device->udev_device, "UDISKS_SYMBOLIC_ICON_NAME");
930 
931   /* ... and scene! */
932   udisks_block_set_hint_partitionable (iface, hint_partitionable);
933   udisks_block_set_hint_system (iface, hint_system);
934   udisks_block_set_hint_ignore (iface, hint_ignore);
935   udisks_block_set_hint_auto (iface, hint_auto);
936   udisks_block_set_hint_name (iface, hint_name);
937   udisks_block_set_hint_icon_name (iface, hint_icon_name);
938   udisks_block_set_hint_symbolic_icon_name (iface, hint_symbolic_icon_name);
939 }
940 
941 /* ---------------------------------------------------------------------------------------------------- */
942 
943 static gchar *
get_slave_sysfs_path(const gchar * sysfs_path)944 get_slave_sysfs_path (const gchar *sysfs_path)
945 {
946   gchar *ret = NULL;
947   gchar **slaves;
948   slaves = udisks_daemon_util_resolve_links (sysfs_path, "slaves");
949   if (slaves != NULL && g_strv_length (slaves) == 1)
950     {
951       ret = g_strdup (slaves[0]);
952     }
953 
954   g_strfreev (slaves);
955   return ret;
956 }
957 
958 /**
959  * udisks_linux_block_update:
960  * @block: A #UDisksLinuxBlock.
961  * @object: The enclosing #UDisksLinuxBlockObject instance.
962  *
963  * Updates the interface.
964  */
965 void
udisks_linux_block_update(UDisksLinuxBlock * block,UDisksLinuxBlockObject * object)966 udisks_linux_block_update (UDisksLinuxBlock       *block,
967                            UDisksLinuxBlockObject *object)
968 {
969   UDisksBlock *iface = UDISKS_BLOCK (block);
970   UDisksDaemon *daemon;
971   GDBusObjectManagerServer *object_manager;
972   UDisksLinuxDevice *device;
973   GUdevDeviceNumber dev;
974   gchar *drive_object_path;
975   UDisksDrive *drive;
976   gchar *s;
977   const gchar *device_file;
978   const gchar *const *symlinks;
979   const gchar *preferred_device_file;
980   const gchar *id_device_file;
981   gboolean media_removable = FALSE;
982   guint64 size;
983   gboolean media_available;
984   gboolean media_change_detected;
985   gboolean read_only;
986   gboolean seems_encrypted = FALSE;
987   guint n;
988   GError *error = NULL;
989 
990   drive = NULL;
991 
992   device = udisks_linux_block_object_get_device (object);
993   if (device == NULL)
994     goto out;
995 
996   daemon = udisks_linux_block_object_get_daemon (object);
997   object_manager = udisks_daemon_get_object_manager (daemon);
998 
999   dev = g_udev_device_get_device_number (device->udev_device);
1000   device_file = g_udev_device_get_device_file (device->udev_device);
1001   symlinks = g_udev_device_get_device_file_symlinks (device->udev_device);
1002 
1003   udisks_block_set_device (iface, device_file);
1004   udisks_block_set_symlinks (iface, symlinks);
1005   udisks_block_set_device_number (iface, dev);
1006 
1007   size = udisks_daemon_util_block_get_size (device->udev_device,
1008                                             &media_available,
1009                                             &media_change_detected);
1010   udisks_block_set_size (iface, size);
1011 
1012   read_only = g_udev_device_get_sysfs_attr_as_boolean (device->udev_device, "ro");
1013   if (!read_only && g_str_has_prefix (g_udev_device_get_name (device->udev_device), "sr"))
1014     read_only = TRUE;
1015   udisks_block_set_read_only (iface, read_only);
1016 
1017   /* dm-crypt
1018    *
1019    * TODO: this might not be the best way to determine if the device-mapper device
1020    *       is a dm-crypt device.. but unfortunately device-mapper keeps all this stuff
1021    *       in user-space and wants you to use libdevmapper to obtain it...
1022    */
1023   udisks_block_set_crypto_backing_device (iface, "/");
1024   if (g_str_has_prefix (g_udev_device_get_name (device->udev_device), "dm-"))
1025     {
1026       gchar *dm_uuid;
1027       dm_uuid = get_sysfs_attr (device->udev_device, "dm/uuid");
1028       if (dm_uuid != NULL &&
1029            (g_str_has_prefix (dm_uuid, "CRYPT-LUKS") || g_str_has_prefix (dm_uuid, "CRYPT-BITLK") || g_str_has_prefix (dm_uuid, "CRYPT-TCRYPT")))
1030         {
1031           gchar *slave_sysfs_path;
1032           slave_sysfs_path = get_slave_sysfs_path (g_udev_device_get_sysfs_path (device->udev_device));
1033 
1034           while (slave_sysfs_path)
1035             {
1036               UDisksLinuxBlockObject *slave_object;
1037               slave_object = find_block_device_by_sysfs_path (object_manager, slave_sysfs_path);
1038               if (slave_object != NULL)
1039                 {
1040                   UDisksEncrypted *enc;
1041 
1042                   udisks_block_set_crypto_backing_device (iface,
1043                                                           g_dbus_object_get_object_path (G_DBUS_OBJECT (slave_object)));
1044 
1045                   /* also set the CleartextDevice property for the parent device */
1046                   enc = udisks_object_peek_encrypted (UDISKS_OBJECT (slave_object));
1047                   if (enc != NULL)
1048                     {
1049                       udisks_encrypted_set_cleartext_device (UDISKS_ENCRYPTED (enc),
1050                                                              g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
1051                     }
1052 
1053                   g_object_unref (slave_object);
1054                   g_free (slave_sysfs_path);
1055                   break;
1056                 }
1057               else
1058                 {
1059                   gchar *old_sysfs_path = slave_sysfs_path;
1060                   slave_sysfs_path = get_slave_sysfs_path (old_sysfs_path);
1061                   g_free (old_sysfs_path);
1062                 }
1063             }
1064         }
1065       g_free (dm_uuid);
1066     }
1067 
1068   /* Sort out preferred device... this is what UI shells should
1069    * display. We default to the block device name.
1070    *
1071    * This is mostly for things like device-mapper where device file is
1072    * a name of the form dm-%d and a symlink name conveys more
1073    * information.
1074    */
1075   preferred_device_file = NULL;
1076   if (g_str_has_prefix (device_file, "/dev/dm-"))
1077     {
1078       const gchar *dm_name;
1079       gchar *dm_name_dev_file = NULL;
1080       const gchar *dm_name_dev_file_as_symlink = NULL;
1081 
1082       const gchar *dm_vg_name;
1083       const gchar *dm_lv_name;
1084       gchar *dm_lvm_dev_file = NULL;
1085 
1086       dm_name = g_udev_device_get_property (device->udev_device, "DM_NAME");
1087       if (dm_name != NULL)
1088         dm_name_dev_file = g_strdup_printf ("/dev/mapper/%s", dm_name);
1089 
1090       dm_vg_name = g_udev_device_get_property (device->udev_device, "DM_VG_NAME");
1091       dm_lv_name = g_udev_device_get_property (device->udev_device, "DM_LV_NAME");
1092       if (dm_vg_name != NULL && dm_lv_name != NULL)
1093         dm_lvm_dev_file =  g_strdup_printf ("/dev/%s/%s", dm_vg_name, dm_lv_name);
1094 
1095       for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
1096         {
1097           if (g_str_has_prefix (symlinks[n], "/dev/vg_")
1098               || g_strcmp0 (symlinks[n], dm_lvm_dev_file) == 0)
1099             {
1100               /* LVM2 */
1101               preferred_device_file = symlinks[n];
1102               break;
1103             }
1104           else if (g_strcmp0 (symlinks[n], dm_name_dev_file) == 0)
1105             {
1106               dm_name_dev_file_as_symlink = symlinks[n];
1107             }
1108         }
1109       /* fall back to /dev/mapper/$DM_NAME, if available as a symlink */
1110       if (preferred_device_file == NULL && dm_name_dev_file_as_symlink != NULL)
1111         preferred_device_file = dm_name_dev_file_as_symlink;
1112       g_free (dm_name_dev_file);
1113       g_free (dm_lvm_dev_file);
1114     }
1115   else if (g_str_has_prefix (device_file, "/dev/md"))
1116     {
1117       for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
1118         {
1119           if (g_str_has_prefix (symlinks[n], "/dev/md/"))
1120             {
1121               preferred_device_file = symlinks[n];
1122               break;
1123             }
1124         }
1125     }
1126   /* fallback to the device name */
1127   if (preferred_device_file == NULL)
1128     preferred_device_file = g_udev_device_get_device_file (device->udev_device);
1129   udisks_block_set_preferred_device (iface, preferred_device_file);
1130 
1131   /* Determine the drive this block device belongs to
1132    *
1133    * TODO: if this is slow we could have a cache or ensure that we
1134    * only do this once or something else
1135    */
1136   drive_object_path = find_drive (object_manager, device->udev_device, &drive);
1137   if (drive_object_path != NULL)
1138     {
1139       udisks_block_set_drive (iface, drive_object_path);
1140       g_free (drive_object_path);
1141     }
1142   else
1143     {
1144       udisks_block_set_drive (iface, "/");
1145     }
1146 
1147   if (drive != NULL)
1148     media_removable = udisks_drive_get_media_removable (drive);
1149 
1150   id_device_file = NULL;
1151   if (media_removable)
1152     {
1153       /* Drive with removable media: determine id by finding a
1154        * suitable /dev/disk/by-uuid symlink (fall back to
1155        * /dev/disk/by-label)
1156        *
1157        * TODO: add features to ata_id / cdrom_id in systemd to extract
1158        *       medium identiers (at optical discs have these) and add
1159        *       udev rules to create symlinks in something like
1160        *       /dev/disk/by-medium. Then use said symlinks to for the
1161        *       id_device_file
1162        */
1163       for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
1164         {
1165           if (g_str_has_prefix (symlinks[n], "/dev/disk/by-uuid/"))
1166             {
1167               id_device_file = symlinks[n];
1168               break;
1169             }
1170           else if (g_str_has_prefix (symlinks[n], "/dev/disk/by-label/"))
1171             {
1172               id_device_file = symlinks[n];
1173             }
1174         }
1175     }
1176   else
1177     {
1178       /* Drive without removable media: determine id by finding a
1179        * suitable /dev/disk/by-id symlink
1180        */
1181       for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
1182         {
1183           if (g_str_has_prefix (symlinks[n], "/dev/disk/by-id/"))
1184             {
1185               id_device_file = symlinks[n];
1186               break;
1187             }
1188         }
1189     }
1190   if (id_device_file != NULL)
1191     {
1192       gchar *id = g_strdup (id_device_file + strlen ("/dev/disk/"));
1193       for (n = 0; id[n] != '\0'; n++)
1194         {
1195           if (id[n] == '/' || id[n] == ' ')
1196             id[n] = '-';
1197         }
1198       udisks_block_set_id (iface, id);
1199       g_free (id);
1200     }
1201   else
1202     {
1203       udisks_block_set_id (iface, NULL);
1204     }
1205 
1206   if (udisks_daemon_get_enable_tcrypt (daemon))
1207     {
1208       seems_encrypted = bd_crypto_device_seems_encrypted (device_file, &error);
1209       if (error != NULL)
1210         {
1211           udisks_warning ("Error determining whether device '%s' seems to be encrypted: %s (%s, %d)",
1212                           device_file, error->message, g_quark_to_string (error->domain), error->code);
1213           g_clear_error (&error);
1214         }
1215     }
1216 
1217   if (seems_encrypted)
1218     {
1219       udisks_block_set_id_usage (iface, "crypto");
1220       udisks_block_set_id_type (iface, "crypto_unknown");
1221     }
1222   else
1223     {
1224       udisks_block_set_id_usage (iface, g_udev_device_get_property (device->udev_device, "ID_FS_USAGE"));
1225       udisks_block_set_id_type (iface, g_udev_device_get_property (device->udev_device, "ID_FS_TYPE"));
1226     }
1227 
1228   s = udisks_decode_udev_string (g_udev_device_get_property (device->udev_device, "ID_FS_VERSION"), NULL);
1229   udisks_block_set_id_version (iface, s);
1230   g_free (s);
1231   s = udisks_decode_udev_string (g_udev_device_get_property (device->udev_device, "ID_FS_LABEL_ENC"),
1232                                  g_udev_device_get_property (device->udev_device, "ID_FS_LABEL"));
1233   udisks_block_set_id_label (iface, s);
1234   g_free (s);
1235   s = udisks_decode_udev_string (g_udev_device_get_property (device->udev_device, "ID_FS_UUID_ENC"),
1236                                  g_udev_device_get_property (device->udev_device, "ID_FS_UUID"));
1237   udisks_block_set_id_uuid (iface, s);
1238   g_free (s);
1239 
1240   update_hints (daemon, block, device, drive);
1241   update_configuration (block, daemon);
1242 #ifdef HAVE_LIBMOUNT_UTAB
1243   update_userspace_mount_options (block, daemon);
1244 #endif
1245   update_mdraid (block, device, drive, object_manager);
1246 
1247  out:
1248   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (block));
1249   if (device != NULL)
1250     g_object_unref (device);
1251   if (drive != NULL)
1252     g_object_unref (drive);
1253 }
1254 
1255 /* ---------------------------------------------------------------------------------------------------- */
1256 
1257 static gboolean
handle_get_secret_configuration(UDisksBlock * _block,GDBusMethodInvocation * invocation,GVariant * options)1258 handle_get_secret_configuration (UDisksBlock           *_block,
1259                                  GDBusMethodInvocation *invocation,
1260                                  GVariant              *options)
1261 {
1262   UDisksLinuxBlock *block = UDISKS_LINUX_BLOCK (_block);
1263   UDisksLinuxBlockObject *object;
1264   UDisksDaemon *daemon;
1265   GVariant *configuration;
1266   GError *error;
1267 
1268   error = NULL;
1269   object = udisks_daemon_util_dup_object (block, &error);
1270   if (object == NULL)
1271     {
1272       g_dbus_method_invocation_take_error (invocation, error);
1273       goto out;
1274     }
1275 
1276   daemon = udisks_linux_block_object_get_daemon (object);
1277 
1278   error = NULL;
1279   configuration = calculate_configuration (block, daemon, TRUE, &error);
1280   if (configuration == NULL)
1281     {
1282       g_dbus_method_invocation_take_error (invocation, error);
1283       goto out;
1284     }
1285 
1286   if (!udisks_daemon_util_check_authorization_sync (daemon,
1287                                                     NULL,
1288                                                     "org.freedesktop.udisks2.read-system-configuration-secrets",
1289                                                     options,
1290                                                     /* Translators: This is shown in an authentcation dialog when
1291                                                      * the user is editing settings that involve system-level
1292                                                      * passwords and secrets
1293                                                      */
1294                                                     N_("Authentication is required to read system-level secrets"),
1295                                                     invocation))
1296     {
1297       g_variant_unref (configuration);
1298       goto out;
1299     }
1300 
1301   udisks_block_complete_get_secret_configuration (UDISKS_BLOCK (block),
1302                                                   invocation,
1303                                                   configuration); /* consumes floating ref */
1304 
1305  out:
1306   g_clear_object (&object);
1307   return TRUE; /* returning TRUE means that we handled the method invocation */
1308 }
1309 
1310 /* ---------------------------------------------------------------------------------------------------- */
1311 
1312 static gchar *
escape_fstab(const gchar * source)1313 escape_fstab (const gchar *source)
1314 {
1315   GString *s;
1316   guint n;
1317   s = g_string_new (NULL);
1318   for (n = 0; source[n] != '\0'; n++)
1319     {
1320       switch (source[n])
1321         {
1322         case ' ':
1323         case '\t':
1324         case '\n':
1325         case '\\':
1326           g_string_append_printf (s, "\\%03o", (guint) source[n]);
1327           break;
1328 
1329         default:
1330           g_string_append_c (s, source[n]);
1331           break;
1332         }
1333     }
1334   return g_string_free (s, FALSE);
1335 }
1336 
1337 /* based on g_strcompress() */
1338 static gchar *
unescape_fstab(const gchar * source)1339 unescape_fstab (const gchar *source)
1340 {
1341   const gchar *p = source, *octal;
1342   gchar *dest = g_malloc (strlen (source) + 1);
1343   gchar *q = dest;
1344 
1345   while (*p)
1346     {
1347       if (*p == '\\')
1348         {
1349           p++;
1350           switch (*p)
1351             {
1352             case '\0':
1353               udisks_warning ("unescape_fstab: trailing \\");
1354               goto out;
1355             case '0':  case '1':  case '2':  case '3':  case '4':
1356             case '5':  case '6':  case '7':
1357               *q = 0;
1358               octal = p;
1359               while ((p < octal + 3) && (*p >= '0') && (*p <= '7'))
1360                 {
1361                   *q = (*q * 8) + (*p - '0');
1362                   p++;
1363                 }
1364               q++;
1365               p--;
1366               break;
1367             default:            /* Also handles \" and \\ */
1368               *q++ = *p;
1369               break;
1370             }
1371         }
1372       else
1373         *q++ = *p;
1374       p++;
1375     }
1376 out:
1377   *q = 0;
1378 
1379   return dest;
1380 }
1381 
1382 /* ---------------------------------------------------------------------------------------------------- */
1383 
1384 static gchar *
make_block_fsname(UDisksBlock * block)1385 make_block_fsname (UDisksBlock *block)
1386 {
1387   const gchar *uuid = udisks_block_get_id_uuid (block);
1388 
1389   if (uuid && *uuid)
1390     return g_strdup_printf ("UUID=%s", uuid);
1391   else
1392     return g_strdup (udisks_block_get_device (block));
1393 }
1394 
1395 static gchar *
track_parents(UDisksBlock * block,const gchar * options)1396 track_parents (UDisksBlock *block, const gchar *options)
1397 {
1398   UDisksObject *object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (block)));
1399   UDisksDaemon *daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
1400 
1401   gchar *new_options, *start, *end, *path;
1402 
1403   /* Remove old x-parent entries
1404    */
1405   new_options = g_strdup (options);
1406   start = new_options;
1407   while ((start = strstr (start, "x-parent=")) != NULL)
1408     {
1409       end = strchr (start, ',');
1410       if (end)
1411         strcpy (start, end+1);
1412       else
1413         *start = '\0';
1414     }
1415 
1416   /* Walk up our ancestry and give each parent a chance to be tracked.
1417    */
1418   path = g_strdup (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
1419   do
1420     {
1421       gchar *uuid = NULL;
1422       gchar *parent_path = udisks_daemon_get_parent_for_tracking (daemon, path, &uuid);
1423 
1424       if (uuid && *uuid)
1425         {
1426           gchar *new;
1427           if (new_options && *new_options)
1428             new = g_strdup_printf ("%s,x-parent=%s", new_options, uuid);
1429           else
1430             new = g_strdup_printf ("x-parent=%s", uuid);
1431           g_free (new_options);
1432           new_options = new;
1433         }
1434 
1435       g_free (uuid);
1436       g_free (path);
1437       path = parent_path;
1438     }
1439   while (path);
1440 
1441   return new_options;
1442 }
1443 
1444 static gboolean
add_remove_fstab_entry(UDisksBlock * block,GVariant * remove,GVariant * add,GError ** error)1445 add_remove_fstab_entry (UDisksBlock *block,
1446                         GVariant    *remove,
1447                         GVariant    *add,
1448                         GError     **error)
1449 {
1450   struct mntent mntent_remove;
1451   struct mntent mntent_add;
1452   gboolean track_parents_flag;
1453   gboolean ret;
1454   gchar *auto_fsname = NULL;
1455   gchar *auto_opts = NULL;
1456   gchar *contents;
1457   gchar **lines;
1458   GString *str;
1459   gboolean removed;
1460   guint n;
1461 
1462   contents = NULL;
1463   lines = NULL;
1464   str = NULL;
1465   ret = FALSE;
1466 
1467   if (remove != NULL)
1468     {
1469       if (!g_variant_lookup (remove, "fsname", "^&ay", &mntent_remove.mnt_fsname) ||
1470           !g_variant_lookup (remove, "dir", "^&ay", &mntent_remove.mnt_dir) ||
1471           !g_variant_lookup (remove, "type", "^&ay", &mntent_remove.mnt_type) ||
1472           !g_variant_lookup (remove, "opts", "^&ay", &mntent_remove.mnt_opts) ||
1473           !g_variant_lookup (remove, "freq", "i", &mntent_remove.mnt_freq) ||
1474           !g_variant_lookup (remove, "passno", "i", &mntent_remove.mnt_passno))
1475         {
1476           g_set_error (error,
1477                        UDISKS_ERROR,
1478                        UDISKS_ERROR_FAILED,
1479                        "Missing fsname, dir, type, opts, freq or passno parameter in entry to remove");
1480           goto out;
1481         }
1482     }
1483 
1484   if (add != NULL)
1485     {
1486       if (!g_variant_lookup (add, "fsname", "^&ay", &mntent_add.mnt_fsname))
1487         {
1488           auto_fsname = make_block_fsname (block);
1489           mntent_add.mnt_fsname = auto_fsname;
1490         }
1491 
1492       if (!g_variant_lookup (add, "dir", "^&ay", &mntent_add.mnt_dir) ||
1493           !g_variant_lookup (add, "type", "^&ay", &mntent_add.mnt_type) ||
1494           !g_variant_lookup (add, "opts", "^&ay", &mntent_add.mnt_opts) ||
1495           !g_variant_lookup (add, "freq", "i", &mntent_add.mnt_freq) ||
1496           !g_variant_lookup (add, "passno", "i", &mntent_add.mnt_passno))
1497         {
1498           g_set_error (error,
1499                        UDISKS_ERROR,
1500                        UDISKS_ERROR_FAILED,
1501                        "Missing dir, type, opts, freq or passno parameter in entry to add");
1502           goto out;
1503         }
1504 
1505       if (strlen (mntent_add.mnt_opts) == 0)
1506         {
1507           g_set_error (error,
1508                        UDISKS_ERROR,
1509                        UDISKS_ERROR_FAILED,
1510                        "opts must not be blank");
1511           goto out;
1512         }
1513 
1514       if (g_variant_lookup (add, "track-parents", "b", &track_parents_flag) &&
1515           track_parents_flag)
1516         {
1517           auto_opts = track_parents (block, mntent_add.mnt_opts);
1518           mntent_add.mnt_opts = auto_opts;
1519         }
1520     }
1521 
1522   if (!g_file_get_contents ("/etc/fstab",
1523                             &contents,
1524                             NULL,
1525                             error))
1526     goto out;
1527 
1528   lines = g_strsplit (contents, "\n", 0);
1529 
1530   str = g_string_new (NULL);
1531   removed = FALSE;
1532   for (n = 0; lines != NULL && lines[n] != NULL; n++)
1533     {
1534       const gchar *line = lines[n];
1535       if (strlen (line) == 0 && lines[n+1] == NULL)
1536         break;
1537       if (remove != NULL && !removed)
1538         {
1539           gchar parsed_fsname[512];
1540           gchar parsed_dir[512];
1541           gchar parsed_type[512];
1542           gchar parsed_opts[512];
1543           gint parsed_freq;
1544           gint parsed_passno;
1545           if (sscanf (line, "%511s %511s %511s %511s %d %d",
1546                       parsed_fsname,
1547                       parsed_dir,
1548                       parsed_type,
1549                       parsed_opts,
1550                       &parsed_freq,
1551                       &parsed_passno) == 6)
1552             {
1553               gchar *unescaped_fsname = unescape_fstab (parsed_fsname);
1554               gchar *unescaped_dir = unescape_fstab (parsed_dir);
1555               gchar *unescaped_type = unescape_fstab (parsed_type);
1556               gchar *unescaped_opts = unescape_fstab (parsed_opts);
1557               gboolean matches = FALSE;
1558               if (g_strcmp0 (unescaped_fsname,   mntent_remove.mnt_fsname) == 0 &&
1559                   g_strcmp0 (unescaped_dir,      mntent_remove.mnt_dir) == 0 &&
1560                   g_strcmp0 (unescaped_type,     mntent_remove.mnt_type) == 0 &&
1561                   g_strcmp0 (unescaped_opts,     mntent_remove.mnt_opts) == 0 &&
1562                   parsed_freq ==      mntent_remove.mnt_freq &&
1563                   parsed_passno ==    mntent_remove.mnt_passno)
1564                 {
1565                   matches = TRUE;
1566                 }
1567               g_free (unescaped_fsname);
1568               g_free (unescaped_dir);
1569               g_free (unescaped_type);
1570               g_free (unescaped_opts);
1571               if (matches)
1572                 {
1573                   removed = TRUE;
1574                   continue;
1575                 }
1576             }
1577         }
1578       g_string_append (str, line);
1579       g_string_append_c (str, '\n');
1580     }
1581 
1582   if (remove != NULL && !removed)
1583     {
1584       g_set_error (error,
1585                    UDISKS_ERROR,
1586                    UDISKS_ERROR_FAILED,
1587                    "Didn't find entry to remove");
1588       goto out;
1589     }
1590 
1591   if (add != NULL)
1592     {
1593       gchar *escaped_fsname = escape_fstab (mntent_add.mnt_fsname);
1594       gchar *escaped_dir = escape_fstab (mntent_add.mnt_dir);
1595       gchar *escaped_type = escape_fstab (mntent_add.mnt_type);
1596       gchar *escaped_opts = escape_fstab (mntent_add.mnt_opts);
1597       g_string_append_printf (str, "%s %s %s %s %d %d\n",
1598                               escaped_fsname,
1599                               escaped_dir,
1600                               escaped_type,
1601                               escaped_opts,
1602                               mntent_add.mnt_freq,
1603                               mntent_add.mnt_passno);
1604       g_free (escaped_fsname);
1605       g_free (escaped_dir);
1606       g_free (escaped_type);
1607       g_free (escaped_opts);
1608     }
1609 
1610   if (!udisks_daemon_util_file_set_contents ("/etc/fstab",
1611                                              str->str,
1612                                              -1,
1613                                              0644, /* mode to use if non-existent */
1614                                              error))
1615     goto out;
1616 
1617   ret = TRUE;
1618 
1619  out:
1620   g_free (auto_opts);
1621   g_free (auto_fsname);
1622   g_strfreev (lines);
1623   g_free (contents);
1624   if (str != NULL)
1625     g_string_free (str, TRUE);
1626   return ret;
1627 }
1628 
1629 /* ---------------------------------------------------------------------------------------------------- */
1630 
1631 static gboolean
has_whitespace(const gchar * s)1632 has_whitespace (const gchar *s)
1633 {
1634   guint n;
1635   g_return_val_if_fail (s != NULL, TRUE);
1636   for (n = 0; s[n] != '\0'; n++)
1637     if (g_ascii_isspace (s[n]))
1638       return TRUE;
1639   return FALSE;
1640 }
1641 
1642 static gchar *
make_block_luksname(UDisksBlock * block,GError ** error)1643 make_block_luksname (UDisksBlock *block, GError **error)
1644 {
1645   gchar *uuid = NULL;
1646 
1647   udisks_linux_block_encrypted_lock (block);
1648   uuid = bd_crypto_luks_uuid (udisks_block_get_device (block), error);
1649   udisks_linux_block_encrypted_unlock (block);
1650 
1651   if (uuid)
1652     {
1653       gchar *ret = g_strdup_printf ("luks-%s", uuid);
1654       g_free (uuid);
1655 
1656       return ret;
1657     }
1658   else
1659     return NULL;
1660 }
1661 
1662 static gboolean
add_remove_crypttab_entry(UDisksBlock * block,GVariant * remove,GVariant * add,GError ** error)1663 add_remove_crypttab_entry (UDisksBlock *block,
1664                            GVariant    *remove,
1665                            GVariant    *add,
1666                            GError     **error)
1667 {
1668   const gchar *remove_name = NULL;
1669   const gchar *remove_device = NULL;
1670   const gchar *remove_passphrase_path = NULL;
1671   const gchar *remove_options = NULL;
1672   const gchar *add_name = NULL;
1673   const gchar *add_device = NULL;
1674   const gchar *add_passphrase_path = NULL;
1675   const gchar *add_options = NULL;
1676   const gchar *add_passphrase_contents = NULL;
1677   gboolean track_parents_flag;
1678   gboolean ret;
1679   gchar *auto_name = NULL;
1680   gchar *auto_device = NULL;
1681   gchar *auto_passphrase_path = NULL;
1682   gchar *auto_opts = NULL;
1683   gchar *contents;
1684   gchar **lines;
1685   GString *str;
1686   gboolean removed;
1687   guint n;
1688 
1689   contents = NULL;
1690   lines = NULL;
1691   str = NULL;
1692   ret = FALSE;
1693 
1694   if (remove != NULL)
1695     {
1696       if (!g_variant_lookup (remove, "name", "^&ay", &remove_name) ||
1697           !g_variant_lookup (remove, "device", "^&ay", &remove_device) ||
1698           !g_variant_lookup (remove, "passphrase-path", "^&ay", &remove_passphrase_path) ||
1699           !g_variant_lookup (remove, "options", "^&ay", &remove_options))
1700         {
1701           g_set_error (error,
1702                        UDISKS_ERROR,
1703                        UDISKS_ERROR_FAILED,
1704                        "Missing name, device, passphrase-path, options or parameter in entry to remove");
1705           goto out;
1706         }
1707     }
1708 
1709   if (add != NULL)
1710     {
1711       if (!g_variant_lookup (add, "name", "^&ay", &add_name))
1712         {
1713           const gchar *uuid = udisks_block_get_id_uuid (block);
1714           if (uuid == NULL || *uuid == '\0')
1715             {
1716               g_set_error (error,
1717                            UDISKS_ERROR,
1718                            UDISKS_ERROR_FAILED,
1719                            "Block device has no UUID, can't determine default name");
1720               goto out;
1721             }
1722 
1723           auto_name = g_strdup_printf ("luks-%s", uuid);
1724           add_name = auto_name;
1725         }
1726 
1727       if (!g_variant_lookup (add, "device", "^&ay", &add_device))
1728         {
1729           auto_device = make_block_fsname (block);
1730           add_device = auto_device;
1731         }
1732 
1733       if (!g_variant_lookup (add, "options", "^&ay", &add_options) ||
1734           !g_variant_lookup (add, "passphrase-contents", "^&ay", &add_passphrase_contents))
1735         {
1736           g_set_error (error,
1737                        UDISKS_ERROR,
1738                        UDISKS_ERROR_FAILED,
1739                        "Missing options or passphrase-contents parameter in entry to add");
1740           goto out;
1741         }
1742 
1743       if (!g_variant_lookup (add, "passphrase-path", "^&ay", &add_passphrase_path))
1744         {
1745           if (*add_passphrase_contents == '\0')
1746             add_passphrase_path = "";
1747           else
1748             {
1749               auto_passphrase_path = g_strdup_printf ("/etc/luks-keys/%s", add_name);
1750               add_passphrase_path = auto_passphrase_path;
1751             }
1752         }
1753 
1754       /* reject strings with whitespace in them */
1755       if (has_whitespace (add_name) ||
1756           has_whitespace (add_device) ||
1757           has_whitespace (add_passphrase_path) ||
1758           has_whitespace (add_options))
1759         {
1760           g_set_error (error,
1761                        UDISKS_ERROR,
1762                        UDISKS_ERROR_FAILED,
1763                        "One of name, device, passphrase-path or options parameter are invalid (whitespace)");
1764           goto out;
1765         }
1766 
1767       if (g_variant_lookup (add, "track-parents", "b", &track_parents_flag) &&
1768           track_parents_flag)
1769         {
1770           auto_opts = track_parents (block, add_options);
1771           add_options = auto_opts;
1772         }
1773     }
1774 
1775   if (!g_file_get_contents ("/etc/crypttab",
1776                             &contents,
1777                             NULL,
1778                             error))
1779     {
1780       if (g_error_matches (*error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
1781         {
1782           contents = g_strdup ("");
1783           g_clear_error (error);
1784         }
1785       else
1786         goto out;
1787     }
1788 
1789   lines = g_strsplit (contents, "\n", 0);
1790 
1791   str = g_string_new (NULL);
1792   removed = FALSE;
1793   for (n = 0; lines != NULL && lines[n] != NULL; n++)
1794     {
1795       const gchar *line = lines[n];
1796       if (strlen (line) == 0 && lines[n+1] == NULL)
1797         break;
1798       if (remove != NULL && !removed)
1799         {
1800           gchar parsed_name[512];
1801           gchar parsed_device[512];
1802           gchar parsed_passphrase_path[512];
1803           gchar parsed_options[512];
1804           guint num_parsed;
1805 
1806           num_parsed = sscanf (line, "%511s %511s %511s %511s",
1807                                parsed_name, parsed_device, parsed_passphrase_path, parsed_options);
1808           if (num_parsed >= 2)
1809             {
1810               if (num_parsed < 3 || g_strcmp0 (parsed_passphrase_path, "none") == 0)
1811                 strcpy (parsed_passphrase_path, "");
1812               if (num_parsed < 4)
1813                 strcpy (parsed_options, "");
1814               if (g_strcmp0 (parsed_name,            remove_name) == 0 &&
1815                   g_strcmp0 (parsed_device,          remove_device) == 0 &&
1816                   g_strcmp0 (parsed_passphrase_path, remove_passphrase_path) == 0 &&
1817                   g_strcmp0 (parsed_options,         remove_options) == 0)
1818                 {
1819                   /* Nuke passphrase file */
1820                   if (strlen (remove_passphrase_path) > 0 && !g_str_has_prefix (remove_passphrase_path, "/dev"))
1821                     {
1822                       /* Is this exploitable? No, 1. the user would have to control
1823                        * the /etc/crypttab file for us to delete it; and 2. editing the
1824                        * /etc/crypttab file requires a polkit authorization that can't
1825                        * be retained (e.g. the user is always asked for the password)..
1826                        */
1827                       if (unlink (remove_passphrase_path) != 0)
1828                         {
1829                           g_set_error (error,
1830                                        UDISKS_ERROR,
1831                                        UDISKS_ERROR_FAILED,
1832                                        "Error deleting file `%s' with passphrase",
1833                                        remove_passphrase_path);
1834                           goto out;
1835                         }
1836                     }
1837                   removed = TRUE;
1838                   continue;
1839                 }
1840             }
1841         }
1842       g_string_append (str, line);
1843       g_string_append_c (str, '\n');
1844     }
1845 
1846   if (remove != NULL && !removed)
1847     {
1848       g_set_error (error,
1849                    UDISKS_ERROR,
1850                    UDISKS_ERROR_FAILED,
1851                    "Didn't find entry to remove");
1852       goto out;
1853     }
1854 
1855   if (add != NULL)
1856     {
1857       /* First write add_passphrase_content to add_passphrase_path,
1858        * if applicable..
1859        *
1860        * Is this exploitable? No, because editing the /etc/crypttab
1861        * file requires a polkit authorization that can't be retained
1862        * (e.g. the user is always asked for the password)...
1863        *
1864        * Just to be on the safe side we only allow writing into the
1865        * directory /etc/luks-keys if create a _new_ entry.
1866        */
1867       if (strlen (add_passphrase_path) > 0)
1868         {
1869           gchar *filename;
1870           if (g_strcmp0 (add_passphrase_path, remove_passphrase_path) == 0)
1871             {
1872               filename = g_strdup (add_passphrase_path);
1873             }
1874           else
1875             {
1876               if (!g_str_has_prefix (add_passphrase_path, "/etc/luks-keys/"))
1877                 {
1878                   g_set_error (error,
1879                                UDISKS_ERROR,
1880                                UDISKS_ERROR_FAILED,
1881                                "Crypttab passphrase file can only be created in the /etc/luks-keys directory");
1882                   goto out;
1883                 }
1884               /* ensure the directory exists */
1885               if (g_mkdir_with_parents ("/etc/luks-keys", 0700) != 0)
1886                 {
1887                   g_set_error (error,
1888                                UDISKS_ERROR,
1889                                UDISKS_ERROR_FAILED,
1890                                "Error creating /etc/luks-keys directory: %m");
1891                   goto out;
1892                 }
1893               /* avoid symlink attacks */
1894               filename = g_strdup_printf ("/etc/luks-keys/%s", strrchr (add_passphrase_path, '/') + 1);
1895             }
1896 
1897           /* Bail if the requested file already exists */
1898           if (g_file_test (filename, G_FILE_TEST_EXISTS))
1899             {
1900                   g_set_error (error,
1901                                UDISKS_ERROR,
1902                                UDISKS_ERROR_FAILED,
1903                                "Refusing to overwrite existing file %s",
1904                                filename);
1905                   g_free (filename);
1906                   goto out;
1907             }
1908 
1909           if (!udisks_daemon_util_file_set_contents (filename,
1910                                                      add_passphrase_contents,
1911                                                      -1,
1912                                                      0600, /* mode to use if non-existent */
1913                                                      error))
1914             {
1915               g_free (filename);
1916               goto out;
1917             }
1918           g_free (filename);
1919         }
1920       g_string_append_printf (str, "%s %s %s %s\n",
1921                               add_name,
1922                               add_device,
1923                               strlen (add_passphrase_path) > 0 ? add_passphrase_path : "none",
1924                               add_options);
1925     }
1926 
1927   if (!udisks_daemon_util_file_set_contents ("/etc/crypttab",
1928                                              str->str,
1929                                              -1,
1930                                              0600, /* mode to use if non-existent */
1931                                              error))
1932     goto out;
1933 
1934   ret = TRUE;
1935 
1936  out:
1937   g_free (auto_opts);
1938   g_free (auto_name);
1939   g_free (auto_device);
1940   g_free (auto_passphrase_path);
1941   g_strfreev (lines);
1942   g_free (contents);
1943   if (str != NULL)
1944     g_string_free (str, TRUE);
1945   return ret;
1946 }
1947 
1948 /* ---------------------------------------------------------------------------------------------------- */
1949 
1950 static void
update_block_fstab(UDisksDaemon * daemon,UDisksLinuxBlock * block,UDisksLinuxBlockObject * object)1951 update_block_fstab (UDisksDaemon           *daemon,
1952                     UDisksLinuxBlock       *block,
1953                     UDisksLinuxBlockObject *object)
1954 {
1955   UDisksLinuxDevice *device;
1956   gchar *drive_object_path;
1957   UDisksDrive *drive = NULL;
1958 
1959   update_configuration (block, daemon);
1960 
1961   /* hints take fstab records in the calculation */
1962   device = udisks_linux_block_object_get_device (object);
1963   drive_object_path = find_drive (udisks_daemon_get_object_manager (daemon), device->udev_device, &drive);
1964   update_hints (daemon, block, device, drive);
1965   g_free (drive_object_path);
1966   g_clear_object (&device);
1967   g_clear_object (&drive);
1968 }
1969 
1970 static gboolean
handle_add_configuration_item(UDisksBlock * _block,GDBusMethodInvocation * invocation,GVariant * item,GVariant * options)1971 handle_add_configuration_item (UDisksBlock           *_block,
1972                                GDBusMethodInvocation *invocation,
1973                                GVariant              *item,
1974                                GVariant              *options)
1975 {
1976   UDisksLinuxBlock *block = UDISKS_LINUX_BLOCK (_block);
1977   UDisksLinuxBlockObject *object;
1978   UDisksDaemon *daemon;
1979   const gchar *type;
1980   GVariant *details = NULL;
1981   GError *error;
1982 
1983   error = NULL;
1984   object = udisks_daemon_util_dup_object (block, &error);
1985   if (object == NULL)
1986     {
1987       g_dbus_method_invocation_take_error (invocation, error);
1988       goto out;
1989     }
1990 
1991   daemon = udisks_linux_block_object_get_daemon (object);
1992 
1993   g_variant_get (item, "(&s@a{sv})", &type, &details);
1994   if (g_strcmp0 (type, "fstab") == 0)
1995     {
1996       if (!udisks_daemon_util_check_authorization_sync (daemon,
1997                                                         NULL,
1998                                                         "org.freedesktop.udisks2.modify-system-configuration",
1999                                                         options,
2000                                                         /* Translators: shown in authentication dialog - do not translate /etc/fstab */
2001                                                         N_("Authentication is required to add an entry to the /etc/fstab file"),
2002                                                         invocation))
2003         goto out;
2004       error = NULL;
2005       if (!add_remove_fstab_entry (_block, NULL, details, &error))
2006         {
2007           g_dbus_method_invocation_take_error (invocation, error);
2008           goto out;
2009         }
2010       update_block_fstab (daemon, block, object);
2011       udisks_block_complete_add_configuration_item (UDISKS_BLOCK (block), invocation);
2012     }
2013   else if (g_strcmp0 (type, "crypttab") == 0)
2014     {
2015       if (!udisks_daemon_util_check_authorization_sync (daemon,
2016                                                         NULL,
2017                                                         "org.freedesktop.udisks2.modify-system-configuration",
2018                                                         options,
2019                                                         /* Translators: shown in authentication dialog - do not tranlsate /etc/crypttab */
2020                                                         N_("Authentication is required to add an entry to the /etc/crypttab file"),
2021                                                         invocation))
2022         goto out;
2023       error = NULL;
2024       if (!add_remove_crypttab_entry (_block, NULL, details, &error))
2025         {
2026           g_dbus_method_invocation_take_error (invocation, error);
2027           goto out;
2028         }
2029       update_configuration (block, daemon);
2030       udisks_block_complete_add_configuration_item (UDISKS_BLOCK (block), invocation);
2031     }
2032   else
2033     {
2034       g_dbus_method_invocation_return_error (invocation,
2035                                              UDISKS_ERROR,
2036                                              UDISKS_ERROR_FAILED,
2037                                              "Only /etc/fstab or /etc/crypttab items can be added");
2038       goto out;
2039     }
2040 
2041  out:
2042   g_variant_unref (details);
2043   g_clear_object (&object);
2044   return TRUE; /* returning TRUE means that we handled the method invocation */
2045 }
2046 
2047 /* ---------------------------------------------------------------------------------------------------- */
2048 
2049 static gboolean
handle_remove_configuration_item(UDisksBlock * _block,GDBusMethodInvocation * invocation,GVariant * item,GVariant * options)2050 handle_remove_configuration_item (UDisksBlock           *_block,
2051                                   GDBusMethodInvocation *invocation,
2052                                   GVariant              *item,
2053                                   GVariant              *options)
2054 {
2055   UDisksLinuxBlock *block = UDISKS_LINUX_BLOCK (_block);
2056   UDisksLinuxBlockObject *object;
2057   UDisksDaemon *daemon;
2058   const gchar *type;
2059   GVariant *details = NULL;
2060   GError *error;
2061 
2062   error = NULL;
2063   object = udisks_daemon_util_dup_object (block, &error);
2064   if (object == NULL)
2065     {
2066       g_dbus_method_invocation_take_error (invocation, error);
2067       goto out;
2068     }
2069 
2070   daemon = udisks_linux_block_object_get_daemon (object);
2071 
2072   g_variant_get (item, "(&s@a{sv})", &type, &details);
2073   if (g_strcmp0 (type, "fstab") == 0)
2074     {
2075       if (!udisks_daemon_util_check_authorization_sync (daemon,
2076                                                         NULL,
2077                                                         "org.freedesktop.udisks2.modify-system-configuration",
2078                                                         options,
2079                                                         /* Translators: shown in authentication dialog - do not translate /etc/fstab */
2080                                                         N_("Authentication is required to remove an entry from /etc/fstab file"),
2081                                                         invocation))
2082         goto out;
2083       error = NULL;
2084       if (!add_remove_fstab_entry (_block, details, NULL, &error))
2085         {
2086           g_dbus_method_invocation_take_error (invocation, error);
2087           goto out;
2088         }
2089       update_block_fstab (daemon, block, object);
2090       udisks_block_complete_remove_configuration_item (UDISKS_BLOCK (block), invocation);
2091     }
2092   else if (g_strcmp0 (type, "crypttab") == 0)
2093     {
2094       if (!udisks_daemon_util_check_authorization_sync (daemon,
2095                                                         NULL,
2096                                                         "org.freedesktop.udisks2.modify-system-configuration",
2097                                                         options,
2098                                                         /* Translators: shown in authentication dialog - do not translate /etc/crypttab */
2099                                                         N_("Authentication is required to remove an entry from the /etc/crypttab file"),
2100                                                         invocation))
2101         goto out;
2102       error = NULL;
2103       if (!add_remove_crypttab_entry (_block, details, NULL, &error))
2104         {
2105           g_dbus_method_invocation_take_error (invocation, error);
2106           goto out;
2107         }
2108       update_configuration (block, daemon);
2109       udisks_block_complete_remove_configuration_item (UDISKS_BLOCK (block), invocation);
2110     }
2111   else
2112     {
2113       g_dbus_method_invocation_return_error (invocation,
2114                                              UDISKS_ERROR,
2115                                              UDISKS_ERROR_FAILED,
2116                                              "Only fstab or crypttab items can be removed");
2117       goto out;
2118     }
2119 
2120  out:
2121   g_variant_unref (details);
2122   g_clear_object (&object);
2123   return TRUE; /* returning TRUE means that we handled the method invocation */
2124 }
2125 
2126 /* ---------------------------------------------------------------------------------------------------- */
2127 
2128 static gboolean
handle_update_configuration_item(UDisksBlock * _block,GDBusMethodInvocation * invocation,GVariant * old_item,GVariant * new_item,GVariant * options)2129 handle_update_configuration_item (UDisksBlock           *_block,
2130                                   GDBusMethodInvocation *invocation,
2131                                   GVariant              *old_item,
2132                                   GVariant              *new_item,
2133                                   GVariant              *options)
2134 {
2135   UDisksLinuxBlock *block = UDISKS_LINUX_BLOCK (_block);
2136   UDisksLinuxBlockObject *object;
2137   UDisksDaemon *daemon;
2138   const gchar *old_type;
2139   const gchar *new_type;
2140   GVariant *old_details = NULL;
2141   GVariant *new_details = NULL;
2142   GError *error;
2143 
2144   error = NULL;
2145   object = udisks_daemon_util_dup_object (block, &error);
2146   if (object == NULL)
2147     {
2148       g_dbus_method_invocation_take_error (invocation, error);
2149       goto out;
2150     }
2151 
2152   daemon = udisks_linux_block_object_get_daemon (object);
2153 
2154   g_variant_get (old_item, "(&s@a{sv})", &old_type, &old_details);
2155   g_variant_get (new_item, "(&s@a{sv})", &new_type, &new_details);
2156   if (g_strcmp0 (old_type, new_type) != 0)
2157     {
2158       g_dbus_method_invocation_return_error (invocation,
2159                                              UDISKS_ERROR,
2160                                              UDISKS_ERROR_FAILED,
2161                                              "old and new item are not of the same type");
2162       goto out;
2163     }
2164 
2165   if (g_strcmp0 (old_type, "fstab") == 0)
2166     {
2167       if (!udisks_daemon_util_check_authorization_sync (daemon,
2168                                                         NULL,
2169                                                         "org.freedesktop.udisks2.modify-system-configuration",
2170                                                         options,
2171                                                         /* Translators: shown in authentication dialog - do not translate /etc/fstab */
2172                                                         N_("Authentication is required to modify the /etc/fstab file"),
2173                                                         invocation))
2174         goto out;
2175       error = NULL;
2176       if (!add_remove_fstab_entry (_block, old_details, new_details, &error))
2177         {
2178           g_dbus_method_invocation_take_error (invocation, error);
2179           goto out;
2180         }
2181       update_block_fstab (daemon, block, object);
2182       udisks_block_complete_update_configuration_item (UDISKS_BLOCK (block), invocation);
2183     }
2184   else if (g_strcmp0 (old_type, "crypttab") == 0)
2185     {
2186       if (!udisks_daemon_util_check_authorization_sync (daemon,
2187                                                         NULL,
2188                                                         "org.freedesktop.udisks2.modify-system-configuration",
2189                                                         options,
2190                                                         /* Translators: shown in authentication dialog - do not translate /etc/crypttab */
2191                                                         N_("Authentication is required to modify the /etc/crypttab file"),
2192                                                         invocation))
2193         goto out;
2194       error = NULL;
2195       if (!add_remove_crypttab_entry (_block, old_details, new_details, &error))
2196         {
2197           g_dbus_method_invocation_take_error (invocation, error);
2198           goto out;
2199         }
2200       update_configuration (block, daemon);
2201       udisks_block_complete_update_configuration_item (UDISKS_BLOCK (block), invocation);
2202     }
2203   else
2204     {
2205       g_dbus_method_invocation_return_error (invocation,
2206                                              UDISKS_ERROR,
2207                                              UDISKS_ERROR_FAILED,
2208                                              "Only fstab or crypttab items can be updated");
2209       goto out;
2210     }
2211 
2212  out:
2213   g_variant_unref (new_details);
2214   g_variant_unref (old_details);
2215   g_clear_object (&object);
2216   return TRUE; /* returning TRUE means that we handled the method invocation */
2217 }
2218 
2219 /* ---------------------------------------------------------------------------------------------------- */
2220 
2221 typedef struct
2222 {
2223   UDisksObject *object;
2224   const gchar  *type;
2225 } FormatWaitData;
2226 
2227 /* ---------------------------------------------------------------------------------------------------- */
2228 
2229 static UDisksObject *
wait_for_filesystem(UDisksDaemon * daemon,gpointer user_data)2230 wait_for_filesystem (UDisksDaemon *daemon,
2231                      gpointer      user_data)
2232 {
2233   FormatWaitData *data = user_data;
2234   UDisksObject *ret = NULL;
2235   UDisksBlock *block = NULL;
2236   UDisksPartitionTable *partition_table = NULL;
2237   UDisksFilesystem *filesystem = NULL;
2238   gchar *id_type = NULL;
2239   gchar *partition_table_type = NULL;
2240 
2241   block = udisks_object_get_block (data->object);
2242   if (block == NULL)
2243     goto out;
2244 
2245   partition_table = udisks_object_get_partition_table (data->object);
2246   filesystem = udisks_object_get_filesystem (data->object);
2247 
2248   id_type = udisks_block_dup_id_type (block);
2249 
2250   if (g_strcmp0 (data->type, "empty") == 0)
2251     {
2252       if ((id_type == NULL || g_strcmp0 (id_type, "") == 0 ||
2253            g_strcmp0 (id_type, "crypto_unknown") == 0) && partition_table == NULL)
2254         {
2255           ret = g_object_ref (data->object);
2256           goto out;
2257         }
2258     }
2259 
2260   if (g_strcmp0 (id_type, data->type) == 0)
2261     {
2262       /* check that we should expect a filesystem and wait for corresponding interface
2263        * to be exported on the object */
2264       if (g_strcmp0 (data->type, "empty") == 0 ||
2265           ! udisks_linux_block_object_contains_filesystem (data->object) ||
2266           filesystem != NULL)
2267         {
2268           ret = g_object_ref (data->object);
2269           goto out;
2270         }
2271     }
2272 
2273   if (partition_table != NULL)
2274     {
2275       partition_table_type = udisks_partition_table_dup_type_ (partition_table);
2276       if (g_strcmp0 (partition_table_type, data->type) == 0)
2277         {
2278           ret = g_object_ref (data->object);
2279           goto out;
2280         }
2281     }
2282 
2283  out:
2284   g_free (partition_table_type);
2285   g_free (id_type);
2286   g_clear_object (&partition_table);
2287   g_clear_object (&filesystem);
2288   g_clear_object (&block);
2289   return ret;
2290 }
2291 
2292 /* ---------------------------------------------------------------------------------------------------- */
2293 
2294 static UDisksObject *
wait_for_luks_uuid(UDisksDaemon * daemon,gpointer user_data)2295 wait_for_luks_uuid (UDisksDaemon *daemon,
2296                     gpointer      user_data)
2297 {
2298   FormatWaitData *data = user_data;
2299   UDisksObject *ret = NULL;
2300   UDisksBlock *block = NULL;
2301 
2302   block = udisks_object_get_block (data->object);
2303   if (block == NULL)
2304     goto out;
2305 
2306   if (g_strcmp0 (udisks_block_get_id_type (block), "crypto_LUKS") != 0)
2307     goto out;
2308 
2309   ret = g_object_ref (data->object);
2310 
2311  out:
2312   g_clear_object (&block);
2313   return ret;
2314 }
2315 
2316 /* ---------------------------------------------------------------------------------------------------- */
2317 
2318 static UDisksObject *
wait_for_luks_cleartext(UDisksDaemon * daemon,gpointer user_data)2319 wait_for_luks_cleartext (UDisksDaemon *daemon,
2320                          gpointer      user_data)
2321 {
2322   FormatWaitData *data = user_data;
2323   UDisksObject *ret = NULL;
2324   GList *objects, *l;
2325 
2326   objects = udisks_daemon_get_objects (daemon);
2327   for (l = objects; l != NULL; l = l->next)
2328     {
2329       UDisksObject *object = UDISKS_OBJECT (l->data);
2330       UDisksBlock *block = NULL;
2331 
2332       block = udisks_object_get_block (object);
2333       if (block != NULL)
2334         {
2335           if (g_strcmp0 (udisks_block_get_crypto_backing_device (block),
2336                          g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object))) == 0)
2337             {
2338               g_object_unref (block);
2339               ret = g_object_ref (object);
2340               goto out;
2341             }
2342           g_object_unref (block);
2343         }
2344     }
2345 
2346  out:
2347   g_list_free_full (objects, g_object_unref);
2348   return ret;
2349 }
2350 
2351 /* ---------------------------------------------------------------------------------------------------- */
2352 
2353 static gboolean
erase_ata_device(UDisksBlock * block,UDisksObject * object,UDisksDaemon * daemon,uid_t caller_uid,gboolean enhanced,GError ** error)2354 erase_ata_device (UDisksBlock   *block,
2355                   UDisksObject  *object,
2356                   UDisksDaemon  *daemon,
2357                   uid_t          caller_uid,
2358                   gboolean       enhanced,
2359                   GError       **error)
2360 {
2361   gboolean ret = FALSE;
2362   UDisksObject *drive_object = NULL;
2363   UDisksDriveAta *ata = NULL;
2364 
2365   drive_object = udisks_daemon_find_object (daemon, udisks_block_get_drive (block));
2366   if (drive_object == NULL)
2367     {
2368       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "No drive object");
2369       goto out;
2370     }
2371   ata = udisks_object_get_drive_ata (drive_object);
2372   if (ata == NULL)
2373     {
2374       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED, "Drive is not an ATA drive");
2375       goto out;
2376     }
2377 
2378   /* sleep a tiny bit here to avoid the secure erase code racing with
2379    * programs spawned by udev
2380    */
2381   g_usleep (500 * 1000);
2382 
2383   ret = udisks_linux_drive_ata_secure_erase_sync (UDISKS_LINUX_DRIVE_ATA (ata),
2384                                                   caller_uid,
2385                                                   enhanced,
2386                                                   error);
2387 
2388  out:
2389   g_clear_object (&ata);
2390   g_clear_object (&drive_object);
2391   return ret;
2392 }
2393 
2394 /* ---------------------------------------------------------------------------------------------------- */
2395 
2396 #define ERASE_SIZE (1 * 1024*1024)
2397 
2398 static gboolean
erase_device(UDisksBlock * block,UDisksObject * object,UDisksDaemon * daemon,uid_t caller_uid,const gchar * erase_type,GError ** error)2399 erase_device (UDisksBlock   *block,
2400               UDisksObject  *object,
2401               UDisksDaemon  *daemon,
2402               uid_t          caller_uid,
2403               const gchar   *erase_type,
2404               GError       **error)
2405 {
2406   gboolean ret = FALSE;
2407   const gchar *device_file = NULL;
2408   UDisksBaseJob *job = NULL;
2409   gint fd = -1;
2410   guint64 size;
2411   guint64 pos;
2412   guchar *buf = NULL;
2413   gint64 time_of_last_signal;
2414   GError *local_error = NULL;
2415 
2416   if (g_strcmp0 (erase_type, "ata-secure-erase") == 0)
2417     {
2418       ret = erase_ata_device (block, object, daemon, caller_uid, FALSE, error);
2419       goto out;
2420     }
2421   else if (g_strcmp0 (erase_type, "ata-secure-erase-enhanced") == 0)
2422     {
2423       ret = erase_ata_device (block, object, daemon, caller_uid, TRUE, error);
2424       goto out;
2425     }
2426   else if (g_strcmp0 (erase_type, "zero") != 0)
2427     {
2428       g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2429                    "Unknown or unsupported erase type `%s'",
2430                    erase_type);
2431       goto out;
2432     }
2433 
2434   device_file = udisks_block_get_device (block);
2435   fd = open (device_file, O_WRONLY | O_SYNC | O_EXCL);
2436   if (fd == -1)
2437     {
2438       g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2439                    "Error opening device %s for erase: %m", device_file);
2440       goto out;
2441     }
2442 
2443   job = udisks_daemon_launch_simple_job (daemon, object, "format-erase", caller_uid, NULL);
2444   udisks_base_job_set_auto_estimate (UDISKS_BASE_JOB (job), TRUE);
2445   udisks_job_set_progress_valid (UDISKS_JOB (job), TRUE);
2446 
2447   if (ioctl (fd, BLKGETSIZE64, &size) != 0)
2448     {
2449       g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2450                    "Error doing BLKGETSIZE64 iotctl on %s: %m", device_file);
2451       goto out;
2452     }
2453 
2454   udisks_job_set_bytes (UDISKS_JOB (job), size);
2455 
2456   buf = g_new0 (guchar, ERASE_SIZE);
2457   pos = 0;
2458   time_of_last_signal = g_get_monotonic_time ();
2459   while (pos < size)
2460     {
2461       size_t to_write;
2462       ssize_t num_written;
2463       gint64 now;
2464 
2465       to_write = MIN (size - pos, ERASE_SIZE);
2466     again:
2467       num_written = write (fd, buf, to_write);
2468       if (num_written == -1 || num_written == 0)
2469         {
2470           if (errno == EINTR)
2471             goto again;
2472           g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2473                        "Error writing %d bytes to %s: %m",
2474                        (gint) to_write, device_file);
2475           goto out;
2476         }
2477       pos += num_written;
2478 
2479       if (g_cancellable_is_cancelled (udisks_base_job_get_cancellable (job)))
2480         {
2481           g_set_error (&local_error, UDISKS_ERROR, UDISKS_ERROR_CANCELLED,
2482                        "Job was canceled");
2483           goto out;
2484         }
2485 
2486       /* only emit D-Bus signal at most once a second */
2487       now = g_get_monotonic_time ();
2488       if (now - time_of_last_signal > G_USEC_PER_SEC)
2489         {
2490           /* TODO: estimation etc. */
2491           udisks_job_set_progress (UDISKS_JOB (job), ((gdouble) pos) / size);
2492           time_of_last_signal = now;
2493         }
2494     }
2495 
2496   ret = TRUE;
2497 
2498  out:
2499   if (job != NULL)
2500     {
2501       if (local_error != NULL)
2502         udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, local_error->message);
2503       else
2504         udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, "");
2505     }
2506   if (local_error != NULL)
2507     g_propagate_error (error, local_error);
2508   g_free (buf);
2509   if (fd != -1)
2510     close (fd);
2511   return ret;
2512 }
2513 
2514 /* ---------------------------------------------------------------------------------------------------- */
2515 
2516 static const struct
2517 {
2518   const gchar *table_type;
2519   const gchar *id_type;
2520   const gchar *partition_type;
2521 } partition_types_by_id[] = {
2522   {"dos", "vfat",         "0x0c"},
2523   {"dos", "ntfs",         "0x07"},
2524   {"dos", "exfat",        "0x07"},
2525   {"dos", "swap",         "0x82"},
2526   {"dos", "ext2",         "0x83"},
2527   {"dos", "ext3",         "0x83"},
2528   {"dos", "ext4",         "0x83"},
2529   {"dos", "xfs",          "0x83"},
2530   {"dos", "btrfs",        "0x83"},
2531   {"dos", "crypto_LUKS",  "0xe8"},
2532   {"dos", "udf",          "0x07"},
2533 
2534   {"gpt", "vfat",         "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"}, /* Microsoft Basic Data */
2535   {"gpt", "ntfs",         "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"},
2536   {"gpt", "exfat",        "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"},
2537   {"gpt", "swap",         "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f"}, /* Linux Swap */
2538   {"gpt", "ext2",         "0fc63daf-8483-4772-8e79-3d69d8477de4"}, /* Linux Filesystem */
2539   {"gpt", "ext3",         "0fc63daf-8483-4772-8e79-3d69d8477de4"},
2540   {"gpt", "ext4",         "0fc63daf-8483-4772-8e79-3d69d8477de4"},
2541   {"gpt", "xfs",          "0fc63daf-8483-4772-8e79-3d69d8477de4"},
2542   {"gpt", "btrfs",        "0fc63daf-8483-4772-8e79-3d69d8477de4"},
2543   {"gpt", "crypto_LUKS",  "ca7d7ccb-63ed-4c53-861c-1742536059cc"},
2544   {"gpt", "udf",          "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7"},
2545 };
2546 
2547 
2548 /* may return NULL if nothing suitable was found */
2549 static const gchar *
determine_partition_type_for_id(const gchar * table_type,const gchar * id_type)2550 determine_partition_type_for_id (const gchar *table_type,
2551                                  const gchar *id_type)
2552 {
2553   const gchar *ret = NULL;
2554   guint n;
2555 
2556   for (n = 0; n < G_N_ELEMENTS (partition_types_by_id); n++)
2557     {
2558       if (g_strcmp0 (partition_types_by_id[n].table_type, table_type) == 0 &&
2559           g_strcmp0 (partition_types_by_id[n].id_type,    id_type) == 0)
2560         {
2561           ret = partition_types_by_id[n].partition_type;
2562           goto out;
2563         }
2564     }
2565  out:
2566   return ret;
2567 }
2568 
2569 /* ---------------------------------------------------------------------------------------------------- */
2570 
2571 typedef gboolean BlockWalker (UDisksDaemon *daemon,
2572                               UDisksBlock  *block,
2573                               gboolean      is_leaf,
2574                               gpointer      user_data,
2575                               GError      **error);
2576 
2577 static UDisksPartitionTable *
peek_partition_table(UDisksDaemon * daemon,UDisksPartition * partition)2578 peek_partition_table (UDisksDaemon    *daemon,
2579                       UDisksPartition *partition)
2580 {
2581   UDisksObject *object;
2582   UDisksPartitionTable *pt;
2583 
2584   object = udisks_daemon_find_object (daemon, udisks_partition_get_table (partition));
2585   pt = object ? udisks_object_peek_partition_table (object) : NULL;
2586 
2587   g_clear_object (&object);
2588   return pt;
2589 }
2590 
2591 static UDisksBlock *
get_cleartext_block(UDisksDaemon * daemon,UDisksBlock * block)2592 get_cleartext_block (UDisksDaemon  *daemon,
2593                      UDisksBlock   *block)
2594 {
2595   UDisksBlock *ret = NULL;
2596   GDBusObject *object;
2597   const gchar *object_path;
2598   GList *objects = NULL;
2599   GList *l;
2600 
2601   object = g_dbus_interface_get_object (G_DBUS_INTERFACE (block));
2602   if (object == NULL)
2603     goto out;
2604 
2605   object_path = g_dbus_object_get_object_path (object);
2606   objects = udisks_daemon_get_objects (daemon);
2607   for (l = objects; l != NULL; l = l->next)
2608     {
2609       UDisksObject *iter_object = UDISKS_OBJECT (l->data);
2610       UDisksBlock *iter_block;
2611 
2612       iter_block = udisks_object_peek_block (iter_object);
2613       if (iter_block == NULL)
2614         continue;
2615 
2616       if (g_strcmp0 (udisks_block_get_crypto_backing_device (iter_block), object_path) == 0)
2617         {
2618           ret = g_object_ref (iter_block);
2619           goto out;
2620         }
2621     }
2622 
2623  out:
2624   g_list_free_full (objects, g_object_unref);
2625   return ret;
2626 }
2627 
2628 static gboolean
walk_block(UDisksDaemon * daemon,UDisksBlock * block,BlockWalker * walker,gpointer user_data,GError ** error)2629 walk_block (UDisksDaemon  *daemon,
2630             UDisksBlock   *block,
2631             BlockWalker   *walker,
2632             gpointer       user_data,
2633             GError       **error)
2634 {
2635   UDisksObject *object;
2636   UDisksBlock *cleartext;
2637   gboolean is_leaf = TRUE;
2638   guint num_parts = 0;
2639 
2640   object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (block)));
2641   if (object != NULL)
2642     {
2643       /* Recurse for all primary and extended partitions if this is a
2644        * partition table, or for all logical partitions if this is a
2645        * extended partition. */
2646 
2647       UDisksPartitionTable *table;
2648       gboolean is_container;
2649 
2650       UDisksPartition *partition = udisks_object_peek_partition (object);
2651       if (partition && udisks_partition_get_is_container (partition))
2652         {
2653           table = peek_partition_table (daemon, partition);
2654           is_container = TRUE;
2655         }
2656       else
2657         {
2658           table = udisks_object_peek_partition_table (object);
2659           is_container = FALSE;
2660         }
2661 
2662       if (table)
2663         {
2664           GList *ps, *l;
2665           ps = udisks_linux_partition_table_get_partitions (daemon, table, &num_parts);
2666           for (l = ps; l != NULL; l = l->next)
2667             {
2668               UDisksPartition *p = UDISKS_PARTITION (l->data);
2669               UDisksObject *o = (UDisksObject *) g_dbus_interface_get_object (G_DBUS_INTERFACE (p));
2670               UDisksBlock *b = o ? udisks_object_peek_block (o) : NULL;
2671               if (b && !is_container == !udisks_partition_get_is_contained (p))
2672                 {
2673                   is_leaf = FALSE;
2674                   if (!walk_block (daemon, b, walker, user_data, error))
2675                     {
2676                       g_list_free_full (ps, g_object_unref);
2677                       return FALSE;
2678                     }
2679                 }
2680             }
2681           g_list_free_full (ps, g_object_unref);
2682         }
2683     }
2684 
2685   cleartext = get_cleartext_block (daemon, block);
2686   if (cleartext)
2687     {
2688       is_leaf = FALSE;
2689       if (!walk_block (daemon, cleartext, walker, user_data, error))
2690         {
2691           g_object_unref (cleartext);
2692           return FALSE;
2693         }
2694       g_object_unref (cleartext);
2695     }
2696 
2697   return walker (daemon, block, is_leaf, user_data, error);
2698 }
2699 
2700 gboolean
udisks_linux_remove_configuration(GVariant * config,GError ** error)2701 udisks_linux_remove_configuration (GVariant  *config,
2702                                    GError   **error)
2703 {
2704   GVariantIter iter;
2705   const gchar *item_type;
2706   GVariant *details;
2707 
2708   udisks_debug ("Removing for teardown: %s", g_variant_print (config, FALSE));
2709 
2710   g_variant_iter_init (&iter, config);
2711   while (g_variant_iter_next (&iter, "(&s@a{sv})", &item_type, &details))
2712     {
2713       if (strcmp (item_type, "fstab") == 0)
2714         {
2715           if (!add_remove_fstab_entry (NULL, details, NULL, error))
2716             {
2717               g_variant_unref (details);
2718               return FALSE;
2719             }
2720         }
2721       else if (strcmp (item_type, "crypttab") == 0)
2722         {
2723           if (!add_remove_crypttab_entry (NULL, details, NULL, error))
2724             {
2725               g_variant_unref (details);
2726               return FALSE;
2727             }
2728         }
2729       g_variant_unref (details);
2730     }
2731 
2732   return TRUE;
2733 }
2734 
2735 struct TeardownData {
2736   GDBusMethodInvocation *invocation;
2737   GVariant              *options;
2738 };
2739 
2740 static gboolean
teardown_block_walker(UDisksDaemon * daemon,UDisksBlock * block,gboolean is_leaf,gpointer user_data,GError ** error)2741 teardown_block_walker (UDisksDaemon  *daemon,
2742                        UDisksBlock   *block,
2743                        gboolean       is_leaf,
2744                        gpointer       user_data,
2745                        GError       **error)
2746 {
2747   struct TeardownData *data = user_data;
2748   UDisksObject *object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (block)));
2749   UDisksEncrypted *enc = udisks_object_peek_encrypted (object);
2750 
2751   if (enc)
2752     {
2753       UDisksBlock *cleartext = get_cleartext_block (daemon, block);
2754       if (cleartext)
2755         {
2756           /* The crypto backing device is unlocked and the cleartext
2757              device has been cleaned up.  Lock the backing device so
2758              that we can format or wipe it later.
2759           */
2760           if (enc && !udisks_linux_encrypted_lock (UDISKS_LINUX_ENCRYPTED (enc),
2761                                                    data->invocation,
2762                                                    data->options,
2763                                                    error))
2764             return FALSE;
2765         }
2766       else
2767         {
2768           /* The crypto backing device is locked and the cleartext
2769              device has not been cleaned up (since it doesn't exist).
2770              Remove its child configuration.
2771           */
2772           if (!udisks_linux_remove_configuration (udisks_encrypted_get_child_configuration (enc), error))
2773               return FALSE;
2774         }
2775     }
2776 
2777   return udisks_linux_remove_configuration (udisks_block_get_configuration (block), error);
2778 }
2779 
2780 gboolean
udisks_linux_block_teardown(UDisksBlock * block,GDBusMethodInvocation * invocation,GVariant * options,GError ** error)2781 udisks_linux_block_teardown (UDisksBlock               *block,
2782                              GDBusMethodInvocation     *invocation,
2783                              GVariant                  *options,
2784                              GError                   **error)
2785 {
2786   UDisksObject *object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (block)));
2787   UDisksDaemon *daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
2788   struct TeardownData data;
2789 
2790   data.invocation = invocation;
2791   data.options = options;
2792   return walk_block (daemon, block, teardown_block_walker, &data, error);
2793 }
2794 
2795 /* ---------------------------------------------------------------------------------------------------- */
2796 
2797 gboolean
udisks_linux_block_is_luks(UDisksBlock * block)2798 udisks_linux_block_is_luks (UDisksBlock *block)
2799 {
2800   return g_strcmp0 (udisks_block_get_id_usage (block), "crypto") == 0 &&
2801          g_strcmp0 (udisks_block_get_id_type (block), "crypto_LUKS") == 0;
2802 }
2803 
2804 gboolean
udisks_linux_block_is_tcrypt(UDisksBlock * block)2805 udisks_linux_block_is_tcrypt (UDisksBlock *block)
2806 {
2807   return g_strcmp0 (udisks_block_get_id_usage (block), "crypto") == 0 &&
2808          g_strcmp0 (udisks_block_get_id_type (block), "crypto_TCRYPT") == 0;
2809 }
2810 
2811 gboolean
udisks_linux_block_is_bitlk(UDisksBlock * block)2812 udisks_linux_block_is_bitlk (UDisksBlock *block)
2813 {
2814   return g_strcmp0 (udisks_block_get_id_usage (block), "crypto") == 0 &&
2815          g_strcmp0 (udisks_block_get_id_type (block), "BitLocker") == 0;
2816 }
2817 
2818 gboolean
udisks_linux_block_is_unknown_crypto(UDisksBlock * block)2819 udisks_linux_block_is_unknown_crypto (UDisksBlock *block)
2820 {
2821   return g_strcmp0 (udisks_block_get_id_usage (block), "crypto") == 0 &&
2822          g_strcmp0 (udisks_block_get_id_type (block), "crypto_unknown") == 0;
2823 }
2824 
2825 /* ---------------------------------------------------------------------------------------------------- */
2826 
2827 void
udisks_linux_block_encrypted_lock(UDisksBlock * block)2828 udisks_linux_block_encrypted_lock (UDisksBlock *block)
2829 {
2830   UDisksLinuxBlock *block_iface = UDISKS_LINUX_BLOCK (block);
2831   g_mutex_lock (&block_iface->encrypted_lock);
2832 }
2833 
2834 void
udisks_linux_block_encrypted_unlock(UDisksBlock * block)2835 udisks_linux_block_encrypted_unlock (UDisksBlock *block)
2836 {
2837   UDisksLinuxBlock *block_iface = UDISKS_LINUX_BLOCK (block);
2838   g_mutex_unlock (&block_iface->encrypted_lock);
2839 }
2840 
2841 /* ---------------------------------------------------------------------------------------------------- */
2842 
2843 static void
handle_format_failure(GDBusMethodInvocation * invocation,GError * error)2844 handle_format_failure (GDBusMethodInvocation *invocation,
2845                        GError *error)
2846 {
2847   udisks_warning ("%s", error->message);
2848   if (invocation != NULL)
2849     g_dbus_method_invocation_take_error (invocation, error);
2850   else
2851     g_clear_error (&error);
2852 }
2853 
2854 static gboolean
add_blocksize(gchar ** command,const gchar * device,GError ** error)2855 add_blocksize (gchar        **command,
2856                const gchar   *device,
2857                GError       **error)
2858 {
2859   gint fd = -1;
2860   gchar *new_cmd = NULL;
2861   gint blksize = 0;
2862   gchar *size_str = NULL;
2863 
2864   fd = open (device, O_RDONLY);
2865   if (fd < 0)
2866     {
2867       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2868                    "Failed to open the device '%s' to get its block size", device);
2869       return FALSE;
2870     }
2871 
2872   if (ioctl (fd, BLKSSZGET, &blksize) < 0)
2873     {
2874       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2875                    "Failed to get block size of the device '%s'", device);
2876       close (fd);
2877       return FALSE;
2878     }
2879   close (fd);
2880 
2881   size_str = g_strdup_printf ("%d", blksize);
2882   new_cmd = udisks_daemon_util_subst_str_and_escape (*command, "$BLOCKSIZE", size_str);
2883   g_free (size_str);
2884   g_free (*command);
2885   *command = new_cmd;
2886 
2887   return TRUE;
2888 }
2889 
2890 static gchar *
build_command(const gchar * template,const gchar * device,const gchar * label,const gchar * options,GError ** error)2891 build_command (const gchar *template,
2892                const gchar *device,
2893                const gchar *label,
2894                const gchar *options,
2895                GError     **error)
2896 {
2897   gchar *tmp, *tmp2, *command;
2898   tmp = udisks_daemon_util_subst_str_and_escape (template, "$DEVICE", device);
2899   tmp2 = udisks_daemon_util_subst_str_and_escape (tmp, "$LABEL", label != NULL ? label : "");
2900   command = udisks_daemon_util_subst_str (tmp2, "$OPTIONS", options != NULL ? options : "");
2901   g_free (tmp);
2902   g_free (tmp2);
2903   if (strstr (command, "$BLOCKSIZE") && ! add_blocksize (&command, device, error))
2904     {
2905       g_free (command);
2906       return NULL;
2907     }
2908 
2909   return command;
2910 }
2911 
2912 static inline gboolean
need_partprobe_after_mkfs(const gchar * fs_type)2913 need_partprobe_after_mkfs (const gchar *fs_type)
2914 {
2915   /* udftools makes fake MBR since the 2.0 release */
2916   return (g_strcmp0 (fs_type, "udf") == 0);
2917 }
2918 
2919 void
udisks_linux_block_handle_format(UDisksBlock * block,GDBusMethodInvocation * invocation,const gchar * type,GVariant * options,void (* complete)(gpointer user_data),gpointer complete_user_data)2920 udisks_linux_block_handle_format (UDisksBlock             *block,
2921                                   GDBusMethodInvocation   *invocation,
2922                                   const gchar             *type,
2923                                   GVariant                *options,
2924                                   void                   (*complete)(gpointer user_data),
2925                                   gpointer                 complete_user_data)
2926 {
2927   FormatWaitData *wait_data = NULL;
2928   UDisksObject *object;
2929   UDisksPartition *partition = NULL;
2930   UDisksPartitionTable *partition_table = NULL;
2931   UDisksObject *cleartext_object = NULL;
2932   UDisksBlock *cleartext_block = NULL;
2933   UDisksLinuxDevice *udev_cleartext_device = NULL;
2934   UDisksBlock *block_to_mkfs = NULL;
2935   UDisksObject *object_to_mkfs = NULL;
2936   UDisksDaemon *daemon;
2937   UDisksState *state = NULL;
2938   UDisksConfigManager *config_manager = NULL;
2939   const gchar *action_id;
2940   const gchar *message;
2941   const FSInfo *fs_info;
2942   const gchar *command_options = NULL;
2943   gchar *command = NULL;
2944   gchar *error_message;
2945   GError *error;
2946   int status;
2947   uid_t caller_uid;
2948   gid_t caller_gid;
2949   gboolean take_ownership = FALSE;
2950   GString *encrypt_passphrase = NULL;
2951   gchar *encrypt_type = NULL;
2952   gchar *erase_type = NULL;
2953   gchar *mapped_name = NULL;
2954   const gchar *label = NULL;
2955   gchar *device_name = NULL;
2956   gboolean was_partitioned = FALSE;
2957   gboolean no_block = FALSE;
2958   gboolean update_partition_type = FALSE;
2959   gboolean dry_run_first = FALSE;
2960   const gchar *partition_type = NULL;
2961   GVariant *config_items = NULL;
2962   gboolean teardown_flag = FALSE;
2963   gboolean no_discard_flag = FALSE;
2964   BDPartTableType part_table_type = BD_PART_TABLE_UNDEF;
2965   UDisksObject *filesystem_object;
2966 
2967   error = NULL;
2968   object = udisks_daemon_util_dup_object (block, &error);
2969   if (object == NULL)
2970     {
2971       g_dbus_method_invocation_take_error (invocation, error);
2972       goto out;
2973     }
2974 
2975   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
2976   state = udisks_daemon_get_state (daemon);
2977   config_manager = udisks_daemon_get_config_manager (daemon);
2978   command = NULL;
2979   error_message = NULL;
2980 
2981   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
2982   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
2983 
2984   g_variant_lookup (options, "take-ownership", "b", &take_ownership);
2985   udisks_variant_lookup_binary (options, "encrypt.passphrase", &encrypt_passphrase);
2986   g_variant_lookup (options, "encrypt.type", "s", &encrypt_type);
2987   g_variant_lookup (options, "erase", "s", &erase_type);
2988   g_variant_lookup (options, "no-block", "b", &no_block);
2989   g_variant_lookup (options, "update-partition-type", "b", &update_partition_type);
2990   g_variant_lookup (options, "dry-run-first", "b", &dry_run_first);
2991   g_variant_lookup (options, "config-items", "@a(sa{sv})", &config_items);
2992   g_variant_lookup (options, "tear-down", "b", &teardown_flag);
2993   g_variant_lookup (options, "no-discard", "b", &no_discard_flag);
2994   g_variant_lookup (options, "label", "&s", &label);
2995 
2996   partition = udisks_object_get_partition (object);
2997   if (partition != NULL)
2998     {
2999       UDisksObject *partition_table_object;
3000 
3001       /* Fail if partition contains a partition table (e.g. Fedora Hybrid ISO).
3002        * See: https://bugs.freedesktop.org/show_bug.cgi?id=76178
3003        */
3004       if (udisks_partition_get_offset (partition) == 0)
3005         {
3006           g_dbus_method_invocation_return_error (invocation,
3007                                                  UDISKS_ERROR,
3008                                                  UDISKS_ERROR_NOT_SUPPORTED,
3009                                                  "This partition cannot be modified because it contains a partition table; please reinitialize layout of the whole device.");
3010           goto out;
3011         }
3012 
3013       partition_table_object = udisks_daemon_find_object (daemon, udisks_partition_get_table (partition));
3014       if (partition_table_object == NULL)
3015         {
3016           g_clear_object (&partition);
3017         }
3018       else
3019         {
3020           partition_table = udisks_object_get_partition_table (partition_table_object);
3021           g_clear_object (&partition_table_object);
3022         }
3023     }
3024   /* figure out partition type to set, if requested */
3025   if (update_partition_type && partition != NULL && partition_table != NULL)
3026     {
3027       partition_type = determine_partition_type_for_id (udisks_partition_table_get_type_ (partition_table),
3028                                                         encrypt_passphrase != NULL ? "crypto_LUKS" : type);
3029     }
3030 
3031   if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL /* GCancellable */, &caller_uid, &error))
3032     {
3033       g_dbus_method_invocation_take_error (invocation, error);
3034       goto out;
3035     }
3036 
3037   if (!udisks_daemon_util_get_user_info (caller_uid, &caller_gid, NULL /* user name */, &error))
3038     {
3039       g_dbus_method_invocation_take_error (invocation, error);
3040       goto out;
3041     }
3042 
3043   if (g_strcmp0 (erase_type, "ata-secure-erase") == 0 ||
3044       g_strcmp0 (erase_type, "ata-secure-erase-enhanced") == 0)
3045     {
3046       /* Translators: Shown in authentication dialog when the user
3047        * requests erasing a hard disk using the SECURE ERASE UNIT
3048        * command.
3049        *
3050        * Do not translate $(drive), it's a placeholder and
3051        * will be replaced by the name of the drive/device in question
3052        */
3053       message = N_("Authentication is required to perform a secure erase of $(drive)");
3054       action_id = "org.freedesktop.udisks2.ata-secure-erase";
3055     }
3056   else
3057     {
3058       /* Translators: Shown in authentication dialog when formatting a
3059        * device. This includes both creating a filesystem or partition
3060        * table.
3061        *
3062        * Do not translate $(drive), it's a placeholder and will
3063        * be replaced by the name of the drive/device in question
3064        */
3065       message = N_("Authentication is required to format $(drive)");
3066       action_id = "org.freedesktop.udisks2.modify-device";
3067       if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
3068         {
3069           if (udisks_block_get_hint_system (block))
3070             {
3071               action_id = "org.freedesktop.udisks2.modify-device-system";
3072             }
3073           else if (!udisks_daemon_util_on_user_seat (daemon, object, caller_uid))
3074             {
3075               action_id = "org.freedesktop.udisks2.modify-device-other-seat";
3076             }
3077         }
3078     }
3079 
3080   /* TODO: Consider just accepting any @type and just running "mkfs -t <type>".
3081    *       There are some obvious security implications by doing this, though
3082    */
3083   fs_info = get_fs_info (type);
3084   if (fs_info == NULL || fs_info->command_create_fs == NULL)
3085     {
3086       g_dbus_method_invocation_return_error (invocation,
3087                    UDISKS_ERROR,
3088                    UDISKS_ERROR_NOT_SUPPORTED,
3089                    "Creation of file system type %s is not supported",
3090                    type);
3091       goto out;
3092     }
3093 
3094   if (!udisks_daemon_util_check_authorization_sync (daemon,
3095                                                     object,
3096                                                     action_id,
3097                                                     options,
3098                                                     message,
3099                                                     invocation))
3100     goto out;
3101 
3102   if ((config_items != NULL || teardown_flag) &&
3103       !udisks_daemon_util_check_authorization_sync (daemon,
3104                                                     NULL,
3105                                                     "org.freedesktop.udisks2.modify-system-configuration",
3106                                                     options,
3107                                                     N_("Authentication is required to modify the system configuration"),
3108                                                     invocation))
3109     goto out;
3110 
3111   was_partitioned = (udisks_object_peek_partition_table (object) != NULL);
3112 
3113   if (teardown_flag)
3114     {
3115       if (!udisks_linux_block_teardown (block, invocation, options, &error))
3116         {
3117           g_dbus_method_invocation_take_error (invocation, error);
3118           goto out;
3119         }
3120     }
3121 
3122   device_name = udisks_block_dup_device (block);
3123 
3124   /* First wipe the device... */
3125   if (! bd_fs_wipe (device_name, TRUE, &error))
3126     {
3127       if (g_error_matches (error, BD_FS_ERROR, BD_FS_ERROR_NOFS))
3128         /* no signature to remove, ignore */
3129         g_clear_error (&error);
3130       else
3131         {
3132           g_dbus_method_invocation_return_error (invocation,
3133                                                  UDISKS_ERROR,
3134                                                  UDISKS_ERROR_FAILED,
3135                                                  "Error wiping device: %s",
3136                                                  error->message);
3137           g_clear_error (&error);
3138           goto out;
3139         }
3140     }
3141 
3142   /* ...then wait until this change has taken effect */
3143   if (was_partitioned)
3144     udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (object));
3145   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
3146                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
3147   wait_data = g_new0 (FormatWaitData, 1);
3148   wait_data->object = object;
3149   wait_data->type = "empty";
3150   filesystem_object = udisks_daemon_wait_for_object_sync (daemon,
3151                                                           wait_for_filesystem,
3152                                                           wait_data,
3153                                                           NULL,
3154                                                           UDISKS_DEFAULT_WAIT_TIMEOUT,
3155                                                           &error);
3156   if (filesystem_object == NULL)
3157     {
3158       g_prefix_error (&error, "Error synchronizing after initial wipe: ");
3159       g_dbus_method_invocation_take_error (invocation, error);
3160       goto out;
3161     }
3162   g_object_unref (filesystem_object);
3163 
3164   if (no_discard_flag && fs_info->option_no_discard)
3165     command_options = fs_info->option_no_discard;
3166 
3167   /* If requested, check whether the ultimate filesystem creation
3168      will succeed before actually getting to work.
3169   */
3170   if (dry_run_first && fs_info->command_validate_create_fs)
3171     {
3172       const gchar *device = udisks_block_get_device (block);
3173       command = build_command (fs_info->command_validate_create_fs, device, label, command_options, &error);
3174       if (command == NULL)
3175         {
3176           handle_format_failure (invocation, error);
3177           goto out;
3178         }
3179 
3180       if (!udisks_daemon_launch_spawned_job_sync (daemon,
3181                                                   object,
3182                                                   "format-mkfs", caller_uid,
3183                                                   NULL, /* cancellable */
3184                                                   0,    /* uid_t run_as_uid */
3185                                                   0,    /* uid_t run_as_euid */
3186                                                   &status,
3187                                                   &error_message,
3188                                                   NULL, /* input_string */
3189                                                   "%s", command))
3190         {
3191           g_dbus_method_invocation_return_error (invocation,
3192                                                  UDISKS_ERROR,
3193                                                  UDISKS_ERROR_FAILED,
3194                                                  "Error creating file system: %s",
3195                                                  error_message);
3196           g_free (error_message);
3197           goto out;
3198         }
3199 
3200       g_free (error_message);
3201       g_free (command);
3202     }
3203 
3204   /* And now create the desired filesystem */
3205   wait_data->type = type;
3206 
3207   if (encrypt_passphrase != NULL)
3208     {
3209       UDisksObject *luks_uuid_object;
3210       CryptoJobData data;
3211       data.device = device_name;
3212       data.passphrase = encrypt_passphrase;
3213 
3214       if (encrypt_type != NULL)
3215         data.type = encrypt_type;
3216       else
3217         data.type = udisks_config_manager_get_encryption (config_manager);
3218 
3219       udisks_linux_block_encrypted_lock (block);
3220 
3221       /* Create it */
3222       if (!udisks_daemon_launch_threaded_job_sync (daemon,
3223                                                    object,
3224                                                    "format-mkfs",
3225                                                    caller_uid,
3226                                                    luks_format_job_func,
3227                                                    &data,
3228                                                    NULL, /* user_data_free_func */
3229                                                    NULL, /* cancellable */
3230                                                    &error))
3231         {
3232           handle_format_failure (invocation, g_error_new (UDISKS_ERROR, UDISKS_ERROR_FAILED,
3233                                  "Error creating LUKS device: %s", error->message));
3234           g_clear_error (&error);
3235           udisks_linux_block_encrypted_unlock (block);
3236           goto out;
3237         }
3238       udisks_linux_block_encrypted_unlock (block);
3239 
3240       /* Wait for the UUID to be set */
3241       luks_uuid_object = udisks_daemon_wait_for_object_sync (daemon,
3242                                                              wait_for_luks_uuid,
3243                                                              wait_data,
3244                                                              NULL,
3245                                                              UDISKS_DEFAULT_WAIT_TIMEOUT,
3246                                                              &error);
3247       if (luks_uuid_object == NULL)
3248         {
3249           g_prefix_error (&error, "Error waiting for LUKS UUID: ");
3250           handle_format_failure (invocation, error);
3251           goto out;
3252         }
3253       g_object_unref (luks_uuid_object);
3254 
3255       /* Open it */
3256       mapped_name = make_block_luksname (block, &error);
3257       if (!mapped_name)
3258         {
3259           g_prefix_error (&error, "Failed to get LUKS UUID: ");
3260           handle_format_failure (invocation, error);
3261           goto out;
3262         }
3263 
3264       udisks_linux_block_encrypted_lock (block);
3265       data.map_name = mapped_name;
3266       data.read_only = FALSE;
3267       if (!udisks_daemon_launch_threaded_job_sync (daemon,
3268                                                    object,
3269                                                    "format-mkfs",
3270                                                    caller_uid,
3271                                                    luks_open_job_func,
3272                                                    &data,
3273                                                    NULL, /* user_data_free_func */
3274                                                    NULL, /* cancellable */
3275                                                    &error))
3276         {
3277           handle_format_failure (invocation, g_error_new (UDISKS_ERROR, UDISKS_ERROR_FAILED,
3278                                  "Error opening LUKS device: %s", error->message));
3279           g_clear_error (&error);
3280           udisks_linux_block_encrypted_unlock (block);
3281           goto out;
3282         }
3283 
3284       udisks_linux_block_encrypted_unlock (block);
3285 
3286       /* Wait for it */
3287       cleartext_object = udisks_daemon_wait_for_object_sync (daemon,
3288                                                              wait_for_luks_cleartext,
3289                                                              wait_data,
3290                                                              NULL,
3291                                                              UDISKS_DEFAULT_WAIT_TIMEOUT,
3292                                                              &error);
3293       if (cleartext_object == NULL)
3294         {
3295           g_prefix_error (&error, "Error waiting for LUKS cleartext device: ");
3296           handle_format_failure (invocation, error);
3297           goto out;
3298         }
3299       cleartext_block = udisks_object_get_block (cleartext_object);
3300       if (cleartext_block == NULL)
3301         {
3302           handle_format_failure (invocation, g_error_new (UDISKS_ERROR, UDISKS_ERROR_FAILED,
3303                                  "LUKS cleartext device does not have block interface"));
3304           goto out;
3305         }
3306 
3307       /* update the unlocked-crypto-dev file */
3308       udev_cleartext_device = udisks_linux_block_object_get_device (UDISKS_LINUX_BLOCK_OBJECT (cleartext_object));
3309       udisks_state_add_unlocked_crypto_dev (state,
3310                                             udisks_block_get_device_number (cleartext_block),
3311                                             udisks_block_get_device_number (block),
3312                                             g_udev_device_get_sysfs_attr (udev_cleartext_device->udev_device, "dm/uuid"),
3313                                             caller_uid);
3314 
3315       object_to_mkfs = cleartext_object;
3316       block_to_mkfs = cleartext_block;
3317     }
3318   else
3319     {
3320       object_to_mkfs = object;
3321       block_to_mkfs = block;
3322     }
3323 
3324   /* complete early, if requested */
3325   if (no_block)
3326     {
3327       complete (complete_user_data);
3328       invocation = NULL;
3329     }
3330 
3331   /* Erase the device, if requested
3332    */
3333   if (erase_type != NULL)
3334     {
3335       if (!erase_device (block_to_mkfs, object_to_mkfs, daemon, caller_uid, erase_type, &error))
3336         {
3337           g_prefix_error (&error, "Error erasing device: ");
3338           handle_format_failure (invocation, error);
3339           goto out;
3340         }
3341     }
3342 
3343   /* Set label, if needed */
3344   if (label != NULL)
3345     {
3346       /* TODO: return an error if label is too long */
3347       if (strstr (fs_info->command_create_fs, "$LABEL") == NULL)
3348         {
3349           handle_format_failure (invocation, g_error_new (UDISKS_ERROR, UDISKS_ERROR_NOT_SUPPORTED,
3350                                  "File system type %s does not support labels", type));
3351           goto out;
3352         }
3353     }
3354 
3355   if (g_strcmp0 (type, "dos") == 0)
3356     part_table_type = BD_PART_TABLE_MSDOS;
3357   else if (g_strcmp0 (type, "gpt") == 0)
3358     part_table_type = BD_PART_TABLE_GPT;
3359 
3360   if (part_table_type == BD_PART_TABLE_UNDEF)
3361     {
3362       /* Build and run mkfs shell command */
3363       const gchar *device = udisks_block_get_device (block_to_mkfs);
3364       command = build_command (fs_info->command_create_fs, device, label, command_options, &error);
3365       if (command == NULL)
3366         {
3367           handle_format_failure (invocation, error);
3368           goto out;
3369         }
3370 
3371       if (!udisks_daemon_launch_spawned_job_sync (daemon,
3372                                                   object_to_mkfs,
3373                                                   "format-mkfs", caller_uid,
3374                                                   NULL, /* cancellable */
3375                                                   0,    /* uid_t run_as_uid */
3376                                                   0,    /* uid_t run_as_euid */
3377                                                   &status,
3378                                                   &error_message,
3379                                                   NULL, /* input_string */
3380                                                   "%s", command))
3381         {
3382           handle_format_failure (invocation, g_error_new (UDISKS_ERROR, UDISKS_ERROR_FAILED,
3383                                  "Error creating file system: %s", error_message));
3384           g_free (error_message);
3385           goto out;
3386         }
3387       g_free (error_message);
3388     }
3389   else
3390     {
3391       /* Create the partition table. */
3392       if (! bd_part_create_table (device_name, part_table_type, TRUE, &error))
3393         {
3394           handle_format_failure (invocation, error);
3395           goto out;
3396         }
3397     }
3398 
3399   /* Set the partition type, if requested */
3400   if (partition_type != NULL && partition != NULL)
3401     {
3402       if (g_strcmp0 (udisks_partition_get_type_ (partition), partition_type) != 0)
3403         {
3404           if (!udisks_linux_partition_set_type_sync (UDISKS_LINUX_PARTITION (partition),
3405                                                      partition_type,
3406                                                      caller_uid,
3407                                                      NULL, /* cancellable */
3408                                                      &error))
3409             {
3410               g_prefix_error (&error, "Error setting partition type after formatting: ");
3411               handle_format_failure (invocation, error);
3412               goto out;
3413             }
3414         }
3415     }
3416 
3417   /* The mkfs program may not generate all the uevents we need - so explicitly
3418    * trigger an event here
3419    */
3420   if (need_partprobe_after_mkfs (type))
3421     udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (object));
3422   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object_to_mkfs),
3423                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
3424   wait_data->object = object_to_mkfs;
3425   filesystem_object = udisks_daemon_wait_for_object_sync (daemon,
3426                                                           wait_for_filesystem,
3427                                                           wait_data,
3428                                                           NULL,
3429                                                           UDISKS_DEFAULT_WAIT_TIMEOUT,
3430                                                           &error);
3431   if (filesystem_object == NULL)
3432     {
3433       g_prefix_error (&error,
3434                       "Error synchronizing after formatting with type `%s': ",
3435                       type);
3436       handle_format_failure (invocation, error);
3437       goto out;
3438     }
3439   g_object_unref (filesystem_object);
3440 
3441   /* Change ownership, if requested and supported */
3442   if (take_ownership && fs_info->supports_owners)
3443     {
3444       if (!take_filesystem_ownership (udisks_block_get_device (block_to_mkfs),
3445                                       type, caller_uid, caller_gid, FALSE, &error))
3446         {
3447           g_prefix_error (&error,
3448                           "Failed to take ownership of newly created filesystem: ");
3449           handle_format_failure (invocation, error);
3450           goto out;
3451         }
3452     }
3453 
3454   /* Add configuration items */
3455   if (config_items)
3456     {
3457       GVariantIter iter;
3458       const gchar *item_type;
3459       GVariant *details;
3460 
3461       g_variant_iter_init (&iter, config_items);
3462       while (g_variant_iter_next (&iter, "(&s@a{sv})", &item_type, &details))
3463         {
3464           if (strcmp (item_type, "fstab") == 0)
3465             {
3466               if (!add_remove_fstab_entry (block_to_mkfs, NULL, details, &error))
3467                 {
3468                   handle_format_failure (invocation, error);
3469                   goto out;
3470                 }
3471             }
3472           else if (strcmp (item_type, "crypttab") == 0)
3473             {
3474               if (!add_remove_crypttab_entry (block, NULL, details, &error))
3475                 {
3476                   handle_format_failure (invocation, error);
3477                   goto out;
3478                 }
3479             }
3480           g_variant_unref (details);
3481         }
3482       update_configuration (UDISKS_LINUX_BLOCK (block), daemon);
3483     }
3484 
3485   if (invocation != NULL)
3486     complete (complete_user_data);
3487 
3488  out:
3489   if (object != NULL)
3490     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
3491   if (state != NULL)
3492     udisks_state_check (state);
3493   g_free (device_name);
3494   g_free (mapped_name);
3495   g_free (command);
3496   if (config_items)
3497     g_variant_unref (config_items);
3498   g_free (erase_type);
3499   udisks_string_wipe_and_free (encrypt_passphrase);
3500   g_free (encrypt_type);
3501   g_clear_object (&cleartext_object);
3502   g_clear_object (&cleartext_block);
3503   g_clear_object (&udev_cleartext_device);
3504   g_free (wait_data);
3505   g_clear_object (&partition_table);
3506   g_clear_object (&partition);
3507   g_clear_object (&object);
3508 }
3509 
3510 struct FormatCompleteData {
3511   UDisksBlock *block;
3512   GDBusMethodInvocation *invocation;
3513 };
3514 
3515 static void
handle_format_complete(gpointer user_data)3516 handle_format_complete (gpointer user_data)
3517 {
3518   struct FormatCompleteData *data = user_data;
3519   udisks_block_complete_format (data->block, data->invocation);
3520 }
3521 
3522 static gboolean
handle_format(UDisksBlock * block,GDBusMethodInvocation * invocation,const gchar * type,GVariant * options)3523 handle_format (UDisksBlock           *block,
3524                GDBusMethodInvocation *invocation,
3525                const gchar           *type,
3526                GVariant              *options)
3527 {
3528   struct FormatCompleteData data;
3529   data.block = block;
3530   data.invocation = invocation;
3531   udisks_linux_block_handle_format (block, invocation, type, options,
3532                                     handle_format_complete, &data);
3533 
3534   return TRUE; /* returning true means that we handled the method invocation */
3535 }
3536 
3537 /* ---------------------------------------------------------------------------------------------------- */
3538 
3539 static gint
open_device(const gchar * device,const gchar * mode,gint flags,GError ** error)3540 open_device (const gchar      *device,
3541              const gchar      *mode,
3542              gint              flags,
3543              GError          **error)
3544 {
3545   gint fd = -1;
3546 
3547   if (flags & O_RDWR || flags & O_RDONLY || flags & O_WRONLY)
3548     {
3549       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3550                    "Using 'O_RDWR', 'O_RDONLY' and 'O_WRONLY' flags is not permitted. "
3551                    "Use 'mode' argument instead.");
3552       goto out;
3553     }
3554 
3555   if (g_strcmp0 (mode, "r") == 0)
3556     flags |= O_RDONLY;
3557   else if (g_strcmp0 (mode, "w") == 0)
3558     flags |= O_WRONLY;
3559   else if (g_strcmp0 (mode, "rw") == 0)
3560     flags |= O_RDWR;
3561   else
3562     {
3563       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3564                    "Unknown mode '%s'", mode);
3565       goto out;
3566     }
3567 
3568   fd = open (device, flags);
3569   if (fd == -1)
3570     {
3571       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
3572                    "Error opening device %s: %m", device);
3573       goto out;
3574     }
3575 
3576  out:
3577   return fd;
3578 }
3579 
3580 /* ---------------------------------------------------------------------------------------------------- */
3581 static gboolean
handle_open_for_backup(UDisksBlock * block,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * options)3582 handle_open_for_backup (UDisksBlock           *block,
3583                         GDBusMethodInvocation *invocation,
3584                         GUnixFDList           *fd_list,
3585                         GVariant              *options)
3586 {
3587   UDisksObject *object;
3588   UDisksDaemon *daemon;
3589   UDisksState *state = NULL;
3590   const gchar *action_id;
3591   const gchar *device;
3592   GUnixFDList *out_fd_list = NULL;
3593   GError *error = NULL;
3594   gint fd = -1;
3595 
3596   object = udisks_daemon_util_dup_object (block, &error);
3597   if (object == NULL)
3598     {
3599       g_dbus_method_invocation_take_error (invocation, error);
3600       goto out;
3601     }
3602 
3603   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
3604   state = udisks_daemon_get_state (daemon);
3605 
3606   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
3607   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
3608 
3609   action_id = "org.freedesktop.udisks2.open-device";
3610   if (udisks_block_get_hint_system (block))
3611     action_id = "org.freedesktop.udisks2.open-device-system";
3612 
3613   if (!udisks_daemon_util_check_authorization_sync (daemon,
3614                                                     object,
3615                                                     action_id,
3616                                                     options,
3617                                                     /* Translators: Shown in authentication dialog when creating a
3618                                                      * disk image file.
3619                                                      *
3620                                                      * Do not translate $(drive), it's a placeholder and will
3621                                                      * be replaced by the name of the drive/device in question
3622                                                      */
3623                                                     N_("Authentication is required to open $(drive) for reading"),
3624                                                     invocation))
3625     goto out;
3626 
3627   device = udisks_block_get_device (UDISKS_BLOCK (block));
3628 
3629   fd = open_device (device, "r", O_CLOEXEC | O_EXCL, &error);
3630   if (fd == -1)
3631     {
3632       g_dbus_method_invocation_take_error (invocation, error);
3633       goto out;
3634     }
3635 
3636   out_fd_list = g_unix_fd_list_new_from_array (&fd, 1);
3637   udisks_block_complete_open_for_backup (block, invocation, out_fd_list, g_variant_new_handle (0));
3638 
3639  out:
3640   if (object != NULL)
3641     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
3642   if (state != NULL)
3643     udisks_state_check (state);
3644   g_clear_object (&out_fd_list);
3645   g_clear_object (&object);
3646   return TRUE; /* returning true means that we handled the method invocation */
3647 }
3648 
3649 /* ---------------------------------------------------------------------------------------------------- */
3650 
3651 static gboolean
handle_open_for_restore(UDisksBlock * block,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * options)3652 handle_open_for_restore (UDisksBlock           *block,
3653                          GDBusMethodInvocation *invocation,
3654                          GUnixFDList           *fd_list,
3655                          GVariant              *options)
3656 {
3657   UDisksObject *object;
3658   UDisksDaemon *daemon;
3659   UDisksState *state = NULL;
3660   const gchar *action_id;
3661   const gchar *device;
3662   GUnixFDList *out_fd_list = NULL;
3663   GError *error;
3664   gint fd;
3665 
3666   error = NULL;
3667   object = udisks_daemon_util_dup_object (block, &error);
3668   if (object == NULL)
3669     {
3670       g_dbus_method_invocation_take_error (invocation, error);
3671       goto out;
3672     }
3673 
3674   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
3675   state = udisks_daemon_get_state (daemon);
3676 
3677   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
3678   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
3679 
3680   action_id = "org.freedesktop.udisks2.open-device";
3681   if (udisks_block_get_hint_system (block))
3682     action_id = "org.freedesktop.udisks2.open-device-system";
3683 
3684   if (!udisks_daemon_util_check_authorization_sync (daemon,
3685                                                     object,
3686                                                     action_id,
3687                                                     options,
3688                                                     /* Translators: Shown in authentication dialog when restoring
3689                                                      * from a disk image file.
3690                                                      *
3691                                                      * Do not translate $(drive), it's a placeholder and will
3692                                                      * be replaced by the name of the drive/device in question
3693                                                      */
3694                                                     N_("Authentication is required to open $(drive) for writing"),
3695                                                     invocation))
3696     goto out;
3697 
3698 
3699   device = udisks_block_get_device (UDISKS_BLOCK (block));
3700 
3701   fd = open_device (device, "w", O_SYNC | O_CLOEXEC | O_EXCL, &error);
3702   if (fd == -1)
3703     {
3704       g_dbus_method_invocation_take_error (invocation, error);
3705       goto out;
3706     }
3707 
3708   out_fd_list = g_unix_fd_list_new_from_array (&fd, 1);
3709   udisks_block_complete_open_for_restore (block, invocation, out_fd_list, g_variant_new_handle (0));
3710 
3711  out:
3712   if (object != NULL)
3713     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
3714   if (state != NULL)
3715     udisks_state_check (state);
3716   g_clear_object (&out_fd_list);
3717   g_clear_object (&object);
3718   return TRUE; /* returning true means that we handled the method invocation */
3719 }
3720 
3721 /* ---------------------------------------------------------------------------------------------------- */
3722 
3723 static gboolean
handle_open_for_benchmark(UDisksBlock * block,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * options)3724 handle_open_for_benchmark (UDisksBlock           *block,
3725                            GDBusMethodInvocation *invocation,
3726                            GUnixFDList           *fd_list,
3727                            GVariant              *options)
3728 {
3729   UDisksObject *object;
3730   UDisksDaemon *daemon;
3731   UDisksState *state = NULL;
3732   const gchar *action_id;
3733   const gchar *device;
3734   GUnixFDList *out_fd_list = NULL;
3735   gboolean opt_writable = FALSE;
3736   GError *error = NULL;
3737   gint fd = -1;
3738   const gchar *open_mode = NULL;
3739   gint open_flags;
3740 
3741   object = udisks_daemon_util_dup_object (block, &error);
3742   if (object == NULL)
3743     {
3744       g_dbus_method_invocation_take_error (invocation, error);
3745       goto out;
3746     }
3747 
3748   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
3749   state = udisks_daemon_get_state (daemon);
3750 
3751   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
3752   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
3753 
3754   action_id = "org.freedesktop.udisks2.open-device";
3755   if (udisks_block_get_hint_system (block))
3756     action_id = "org.freedesktop.udisks2.open-device-system";
3757 
3758   if (!udisks_daemon_util_check_authorization_sync (daemon,
3759                                                     object,
3760                                                     action_id,
3761                                                     options,
3762                                                     /* Translators: Shown in authentication dialog when an application
3763                                                      * wants to benchmark a device.
3764                                                      *
3765                                                      * Do not translate $(drive), it's a placeholder and will
3766                                                      * be replaced by the name of the drive/device in question
3767                                                      */
3768                                                     N_("Authentication is required to open $(drive) for benchmarking"),
3769                                                     invocation))
3770     goto out;
3771 
3772   g_variant_lookup (options, "writable", "b", &opt_writable);
3773 
3774   open_flags = O_DIRECT | O_SYNC | O_CLOEXEC;
3775   if (opt_writable)
3776     {
3777       open_flags |= O_EXCL;
3778       open_mode = "rw";
3779     }
3780   else
3781     open_mode = "r";
3782 
3783   device = udisks_block_get_device (UDISKS_BLOCK (block));
3784 
3785   fd = open_device (device, open_mode, open_flags, &error);
3786   if (fd == -1)
3787     {
3788       g_dbus_method_invocation_take_error (invocation, error);
3789       goto out;
3790     }
3791 
3792   out_fd_list = g_unix_fd_list_new_from_array (&fd, 1);
3793   udisks_block_complete_open_for_benchmark (block, invocation, out_fd_list, g_variant_new_handle (0));
3794 
3795  out:
3796   if (object != NULL)
3797     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
3798   if (state != NULL)
3799     udisks_state_check (state);
3800   g_clear_object (&out_fd_list);
3801   g_clear_object (&object);
3802   return TRUE; /* returning true means that we handled the method invocation */
3803 }
3804 
3805 /* ---------------------------------------------------------------------------------------------------- */
3806 
3807 static gboolean
handle_open_device(UDisksBlock * block,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,const gchar * mode,GVariant * options)3808 handle_open_device (UDisksBlock           *block,
3809                     GDBusMethodInvocation *invocation,
3810                     GUnixFDList           *fd_list,
3811                     const gchar           *mode,
3812                     GVariant              *options)
3813 {
3814   UDisksObject *object;
3815   UDisksDaemon *daemon;
3816   UDisksState *state = NULL;
3817   const gchar *action_id;
3818   const gchar *device;
3819   GUnixFDList *out_fd_list = NULL;
3820   GError *error = NULL;
3821   gint fd = -1;
3822   gint flags = 0;
3823 
3824   object = udisks_daemon_util_dup_object (block, &error);
3825   if (object == NULL)
3826     {
3827       g_dbus_method_invocation_take_error (invocation, error);
3828       goto out;
3829     }
3830 
3831   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
3832   state = udisks_daemon_get_state (daemon);
3833 
3834   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
3835   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
3836 
3837   action_id = "org.freedesktop.udisks2.open-device";
3838   if (udisks_block_get_hint_system (block))
3839     action_id = "org.freedesktop.udisks2.open-device-system";
3840 
3841   if (!udisks_daemon_util_check_authorization_sync (daemon,
3842                                                     object,
3843                                                     action_id,
3844                                                     options,
3845                                                     /* Translators: Shown in authentication dialog when an application
3846                                                      * wants to benchmark a device.
3847                                                      *
3848                                                      * Do not translate $(drive), it's a placeholder and will
3849                                                      * be replaced by the name of the drive/device in question
3850                                                      */
3851                                                     N_("Authentication is required to open $(drive)."),
3852                                                     invocation))
3853     goto out;
3854 
3855   device = udisks_block_get_device (UDISKS_BLOCK (block));
3856 
3857   g_variant_lookup (options, "flags", "i", &flags);
3858 
3859   fd = open_device (device, mode, flags, &error);
3860   if (fd == -1)
3861     {
3862       g_dbus_method_invocation_take_error (invocation, error);
3863       goto out;
3864     }
3865 
3866   out_fd_list = g_unix_fd_list_new_from_array (&fd, 1);
3867   udisks_block_complete_open_device (block, invocation, out_fd_list, g_variant_new_handle (0));
3868 
3869  out:
3870   if (object != NULL)
3871     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
3872   if (state != NULL)
3873     udisks_state_check (state);
3874   g_clear_object (&out_fd_list);
3875   g_clear_object (&object);
3876   return TRUE; /* returning true means that we handled the method invocation */
3877 }
3878 
3879 /* ---------------------------------------------------------------------------------------------------- */
3880 
3881 static gboolean
handle_rescan(UDisksBlock * block,GDBusMethodInvocation * invocation,GVariant * options)3882 handle_rescan (UDisksBlock           *block,
3883                GDBusMethodInvocation *invocation,
3884                GVariant              *options)
3885 {
3886   UDisksObject *object = NULL;
3887   UDisksLinuxDevice *device = NULL;
3888   UDisksDaemon *daemon;
3889   const gchar *action_id;
3890   const gchar *message;
3891   GError *error = NULL;
3892 
3893   object = udisks_daemon_util_dup_object (block, &error);
3894   if (object == NULL)
3895     {
3896       g_dbus_method_invocation_take_error (invocation, error);
3897       goto out;
3898     }
3899 
3900   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
3901 
3902   /* Translators: Shown in authentication dialog when an application
3903    * wants to rescan a device.
3904    *
3905    * Do not translate $(drive), it's a placeholder and will
3906    * be replaced by the name of the drive/device in question
3907    */
3908   message = N_("Authentication is required to rescan $(drive)");
3909   action_id = "org.freedesktop.udisks2.rescan";
3910 
3911   if (!udisks_daemon_util_check_authorization_sync (daemon,
3912                                                     object,
3913                                                     action_id,
3914                                                     options,
3915                                                     message,
3916                                                     invocation))
3917     goto out;
3918 
3919   device = udisks_linux_block_object_get_device (UDISKS_LINUX_BLOCK_OBJECT (object));
3920 
3921   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
3922                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
3923   if (g_strcmp0 (g_udev_device_get_devtype (device->udev_device), "disk") == 0)
3924     udisks_linux_block_object_reread_partition_table (UDISKS_LINUX_BLOCK_OBJECT (object));
3925 
3926   udisks_block_complete_rescan (block, invocation);
3927 
3928  out:
3929   g_clear_object (&device);
3930   g_clear_object (&object);
3931   return TRUE; /* returning true means that we handled the method invocation */
3932 }
3933 
3934 /* ---------------------------------------------------------------------------------------------------- */
3935 
3936 static void
block_iface_init(UDisksBlockIface * iface)3937 block_iface_init (UDisksBlockIface *iface)
3938 {
3939   iface->handle_get_secret_configuration  = handle_get_secret_configuration;
3940   iface->handle_add_configuration_item    = handle_add_configuration_item;
3941   iface->handle_remove_configuration_item = handle_remove_configuration_item;
3942   iface->handle_update_configuration_item = handle_update_configuration_item;
3943   iface->handle_format                    = handle_format;
3944   iface->handle_open_for_backup           = handle_open_for_backup;
3945   iface->handle_open_for_restore          = handle_open_for_restore;
3946   iface->handle_open_for_benchmark        = handle_open_for_benchmark;
3947   iface->handle_open_device               = handle_open_device;
3948   iface->handle_rescan                    = handle_rescan;
3949 }
3950